openapi: 3.0.0 info: description: "Clubhouse API" title: "Clubhouse API" version: "1" servers: - url: "https://www.clubhouseapi.com/api/" # User-Agent: clubhouse/269 (iPhone; iOS 14.1; Scale/3.00) # Content-Type: application/json; charset=utf-8 # CH-Languages: en-US # CH-Locale: en_US # CH-AppVersion: 0.1.25 # CH-AppBuild: 269 # CH-DeviceId: # Needed for authenticated APIs: # CH-UserID: 1234 # Authorization: Token # (or Authorization: Bearer if you want to use the JWT auth) # Clubhouse aggressively bans new user signups based partially on headers: see # https://github.com/zhuowei/ClubhouseAPI/issues/2 # curl -H "User-Agent: Clubhouse/269" -H "Content-Type: application/json" -D - -H "CH-Locale: en_US" -H "CH-AppVersion: 0.2.15" -H "CH-AppBuild: 269" # The paginated GET apis are also accessible via POST paths: /start_phone_number_auth: post: summary: Starts phone number auth. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"phone_number": "+11234567890"} responses: 200: description: success content: application/json: examples: success: value: {"success":true,"is_blocked":false,"error_message":null} failure: value: {"success":false,"is_blocked":false,"error_message":""} phoneBlocked: # Could be caused if app version header not present # or if account banned # I've had an account auto-banned after the first login ever # I'm not sure why # This happened to an actual Clubhouse user from the app as well: # https://www.reddit.com/r/ClubhouseApp/comments/lc2d89/got_invited_but_cannot_sign_in/ value: {"success":true,"is_blocked":true} beingThrottled: # You'll get `number_of_attempts_remaining` before you being throttled value: {"success":true,"is_blocked":false,"number_of_attempts_remaining":3} throttled: value: {"success": false, "error_message": "Our systems have detected high usage of this feature from your account. Please try again later."} /call_phone_number_auth: post: summary: Call phone number auth. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {} responses: 200: description: success content: application/json: examples: jsonObject: {} /resend_phone_number_auth: post: summary: Resend phone number auth. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {} responses: 200: description: success content: application/json: examples: jsonObject: {} /complete_phone_number_auth: post: summary: Call phone number auth. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"verification_code": "1234", "phone_number": "+1234567890"} responses: 200: description: success content: application/json: examples: success: value: {"success":true,"is_verified":true,"user_profile":{"user_id":1234,"name":null,"photo_url":null,"username":null},"auth_token":"","refresh_token":"","access_token":"","is_waitlisted":true,"is_onboarding":true} /check_waitlist_status: post: summary: checks waitlist status. responses: 200: description: got waitlist status content: application/json: examples: waitlisted: value: {"is_waitlisted":true,"is_onboarding":true,"success":true} 401: description: authorization not provided content: application/json: examples: error: value: {"detail":"Authentication credentials were not provided."} /me: post: summary: gets user requestBody: content: application/json: schema: type: object examples: clubhouseApp: value: {"return_blocked_ids":true,"timezone_identifier":"America/Los_Angeles","return_following_ids":true} emptyAlsoWorks: value: {} responses: 200: description: the response content: application/json: examples: examples: value: {"has_unread_notifications":false,"actionable_notifications_count":0,"num_invites":0,"auth_token":"","refresh_token":"","access_token":"","notifications_enabled":false,"user_profile":{"user_id":1234,"name":null,"photo_url":null,"username":null},"following_ids":null,"blocked_ids":null,"is_admin":false,"email":null,"feature_flags":[""],"success":true} 403: description: error response - happens if account is banned (is_blocked) content: application/json: examples: examples: value: {"detail": "You do not have permission to perform this action."} /get_release_notes: post: summary: gets release notes. responses: 200: description: the response content: application/json: examples: latest: value: {"should_display":true,"title":"🕹 You are now using the latest update of Clubhouse!","action":"See What's New","url":"https://whatsnew.joinclubhouse.com"} /get_all_topics: get: summary: gets all topics. responses: 200: description: a list of topics content: application/json: examples: latest: value: { "topics": [{ "title": "Example", "id": 1, "abbreviated_title": "Example", "topics": [{ "title": "⏳ The Future", "id": 140, "abbreviated_title": "The Future" }] }] } /get_topic: post: summary: looks up topic by ID. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"topic_id": 140} # this also can take a club_id but I can't get it to work responses: 200: description: returns topic info content: application/json: examples: topic: value: {"topic":{"title":"⏳ The Future","id":140,"abbreviated_title":"The Future"},"success":true} /get_clubs_for_topic: post: summary: looks up clubs by topic. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"topic_id": 140} responses: 200: description: returns list of clubs with pagination content: application/json: examples: clubs: value: {"clubs":[{"club_id":123,"name":"Example Club","description":"Example","photo_url":"https://clubhouseprod.s3.amazonaws.com:443/club___thumbnail_250x250","num_members":123,"num_followers":456,"is_member":false,"is_follower":false}],"count":1,"next":null,"previous":null,"success":true} /get_profile: post: summary: looks up user profile by ID. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"user_id": 4075733} responses: 200: description: returns user profile info content: application/json: examples: user_profile: # invited_by_user_profile can be null if the queried user is not norminated by someone value: { "user_profile": { "user_id": 4075733, "name": "Junho Yeo", "displayname": null, "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/_", "username": "junhoyeo", "bio": "🌸 Creating INEVITABLE™ Services", "twitter": null, "instagram": "_junhoyeo", "num_followers": 103, "num_following": 98, "time_created": "2021-01-28T12:30:25.625451+00:00", "follows_me": false, "is_blocked_by_network": false, "mutual_follows_count": 0, "mutual_follows": [], "notification_type": null, "invited_by_user_profile": { "user_id": 1234, "name": "Example Norminator", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/__thumbnail_250x250", "username": "myexamplenorminator" }, "clubs": [ { "club_id": 12345, "name": "Club name", "description": "Club description", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/club___thumbnail_250x250", "num_members": 123, "num_followers": 456, "enable_private": true, "is_follow_allowed": true, "is_membership_private": false, "is_community": false, "rules": [{ "desc": "Description", "title": "Rule" }], "num_online": 0 }, ], "has_verified_email": true, "can_edit_username": true, "can_edit_name": true, "can_edit_displayname": true, "topics": [ { "title": "🦄 Startups", "id": 107, "abbreviated_title": "Startups" } ] }, "success": true } /get_users_for_topic: get: summary: looks up users by topic. parameters: - in: query name: topic_id schema: type: integer example: 140 - in: query name: page_size schema: type: integer example: 25 - in: query name: page schema: type: integer example: 1 responses: 200: description: returns list of users with pagination. Bios truncated to 80 chars. content: application/json: examples: users: value: {"users":[{"user_id":1234,"name":"John Example","photo_url":"https://clubhouseprod.s3.amazonaws.com:443/__thumbnail_250x250","username":"john_example","bio":"John Example is an exemplary user object","twitter":"john_example"}],"count":1,"next":null,"previous":null,"success":true} /get_channels: get: summary: get all channels # also works with POST on an empty {} request body? responses: 200: description: list of channels content: application/json: examples: channels: value: { "channels": [{ "creator_user_profile_id": 1234, "channel_id": 123456, "channel": "abcdefgh", "topic": "Example Topic", "is_private": false, "is_social_mode": false, "url": "https://www.joinclubhouse.com/room/abcdefgh", "club": { "club_id": 12345, "name": "Club name", "description": "Club description", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/club___thumbnail_250x250", "num_members": 123, "num_followers": 456, "enable_private": true, "is_follow_allowed": true, "is_membership_private": false, "is_community": false, "rules": [{ "desc": "Description", "title": "Rule" }], "num_online": 0 }, "club_name": "Club name", "club_id": 12345, "welcome_for_user_profile": null, "num_other": 0, "has_blocked_speakers": false, "is_explore_channel": false, "num_speakers": 123, "num_all": 1234, "users": [{ "user_id": 1234, "name": "John Example", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/__thumbnail_250x250", "is_speaker": true, "is_moderator": true, "time_joined_as_speaker": "2021-01-20T00:00:00.000000+00:00", "is_followed_by_speaker": true, "is_invited_as_speaker": false }] }], "events": [], "success": true } /join_channel: post: summary: join a channel. requestBody: content: application/json: schema: type: object examples: joinFromAppHomeScreen: # just channel is enough # attribution_details is base64-encoded JSON: # {"is_explore":false,"rank":1} value: {"channel":"abcdefgh","attribution_source":"feed","attribution_details":"eyJpc19leHBsb3JlIjpmYWxzZSwicmFuayI6MX0="} justChannelIsEnough: value: {"channel":"abcdefgh"} responses: 200: description: stuff needed to actually join a channel content: application/json: examples: channel: value: { "creator_user_profile_id": 1234, "channel_id": 123456, "channel": "abcdefgh", "topic": "Channel topic", "is_private": false, "is_social_mode": false, "url": "https://www.joinclubhouse.com/room/abcdefgh", "club": { "club_id": 1234, "name": "Club name", "description": "Club description", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/club___thumbnail_250x250", "num_members": 123, "num_followers": 567, "enable_private": true, "is_follow_allowed": true, "is_membership_private": false, "is_community": false, "rules": [], "num_online": 0 }, "club_name": "Startup Club", "club_id": 45, "welcome_for_user_profile": null, "is_handraise_enabled": true, "handraise_permission": 1, "is_club_member": false, "is_club_admin": false, "users": [{ "user_id": 123456, "name": "John Example", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/__thumbnail_250x250", "username": "john_example", "first_name": "John", "skintone": 3, "is_new": true, "is_speaker": true, "is_moderator": true, "time_joined_as_speaker": "2021-01-31T00:00:00.000000+00:00", "is_followed_by_speaker": true, "is_invited_as_speaker": true }, { "user_id": 1234, "name": "The currently joining user", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/__thumbnail_250x250", "username": "currentuser", "first_name": "Current", "skintone": 3, "is_new": false, "is_speaker": false, "is_moderator": false, "time_joined_as_speaker": null, "is_followed_by_speaker": false, "is_invited_as_speaker": false }], "success": true, "is_empty": false, "token": "", "rtm_token": "", "pubnub_token": "", "pubnub_origin": null, "pubnub_heartbeat_value": 60, "pubnub_heartbeat_interval": 29, "pubnub_enable": true, "agora_native_mute": true } 400: description: cannot join content: application/json: examples: appTooOld: value: {"error_message":"Please upgrade the app to join rooms!","success":false} roomNotAvailable: # Also sent when channel ID is invalid? value: {"success":false,"error_message":"That room is no longer available 👋🏼\nTry starting a new one instead?"} /leave_channel: post: summary: leave a channel. requestBody: content: application/json: schema: type: object examples: jsonObject: # just channel is enough value: {"channel": "abcdefgh"} responses: 200: description: left the channel content: application/json: examples: channel: value: {"success":true} /update_username: post: summary: edits username. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"username": "hipsterhouse"} responses: 200: description: username successfully set content: application/json: examples: success: value: {"success":true,"error_message":null} 400: description: invalid/taken username (eg empty string) content: application/json: examples: emptyString: value: {"success":false,"error_message":"This username is not allowed. Please try another one."} taken: value: {"success":false,"error_message":"This username has already been taken by a different user."} /follow: post: summary: follows a user requestBody: content: application/json: schema: type: object examples: jsonObject: # There's also supposed to be source_topic_id, source, user_ids; not sure what they're for value: {"user_id": 1234} responses: 401: description: waitlisted content: application/json: examples: waitlisted: value: {"success":false,"error_message":"Your account is waitlisted. Please contact us if this is in error."} /refresh_token: post: summary: gets an access_token from a refresh_token. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"refresh": ""} responses: 200: description: returns new access and refresh token content: application/json: examples: validToken: value: {"access":"","refresh":""} 401: description: invalid refresh token content: application/json: examples: invalidToken: # seems it's using https://github.com/SimpleJWT/django-rest-framework-simplejwt value: {"detail":"Token is invalid or expired","code":"token_not_valid"} /get_suggested_invites: post: summary: find users to invite based on phone number. description: (also see https://zerforschung.org/posts/clubhouse-telefonnummern-en/ for @zerforschung's analysis of the privacy implications of this API) requestBody: content: application/json: schema: type: object examples: hasContact: value: {"club_id":null,"upload_contacts":false,"contacts":[{"phone_number": "+11234567890"}]} responses: 200: description: returns list of users that can be invited. content: application/json: examples: validUsers: value: {"num_invites":0,"suggested_invites":[{"phone_number":"+11234567890","in_app":false,"is_invited":false,"num_friends":0}],"success":true} /get_suggested_club_invites: post: summary: find users to invite to clubs based on phone number requestBody: content: application/json: schema: type: object examples: jsonObject: # might also take club_id (number) and upload_contacts (bool)? value: { "upload_contacts": true, "contacts": [{"name": "aaa", "phone_number": "+11234567890"}]} responses: 200: # I couldn't get this to return anything else. description: returns list of suggested invites. content: application/json: examples: empty: value: {"suggested_invites":[],"success":true} /get_club: post: summary: gets club by id requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"club_id": 1234} responses: 200: description: returns club object content: application/json: examples: club: value: {"club":{"club_id":123,"name":"Example Club","description":"Example","photo_url":"https://clubhouseprod.s3.amazonaws.com:443/club___thumbnail_250x250","num_members":123,"num_followers":456,"is_member":false,"is_follower":false},"is_admin":false,"is_member":false,"is_follower":false,"is_pending_accept":false,"is_pending_approval":false,"added_by_user_profile":null,"member_user_ids":[],"num_invites":0,"invite_link":null,"topics":[{"title":"Topic Example","id":140,"abbreviated_title":"Topic Example"}],"success":true} /check_for_update: get: summary: Clubhouse uses this to check for updates when app is not installed from App Store (eg TestFlight) parameters: - in: query name: is_testflight schema: type: integer example: 1 responses: 200: description: successful response content: application/json: examples: noUpdate: value: {"has_update":false,"success":true} hasUpdate: value: {"has_update":true,"is_mandatory":true,"app_url":"https://apps.apple.com/us/app/id1503133294","app_version":"0.1.24","app_build":262,"success":true} /invite_to_app: post: summary: invite a user to the app, using one of your invites requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"name":"John Smith","message":null,"phone_number":"+11234567890"} responses: 400: description: your own account doesn't have an invite either content: application/json: examples: waitlisted: value: {"success":false,"error_message":"Your account is waitlisted. Please contact us if this is in error."} /invite_from_waitlist: post: summary: wave to another user on the waitlist to give them access requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"user_id": 1234} responses: 400: description: your own account is waitlisted, or some other error occurred content: application/json: examples: waitlisted: value: {"success":false,"error_message":"Your account is waitlisted. Please contact us if this is in error."} someOtherError: # https://github.com/zhuowei/ClubhouseAPI/issues/3#issuecomment-776295696 value: {"success": false,"error_message":""} /get_following: post: summary: get a list of the users and clubs that this user is following. Returned users have bios truncated to ~80 characters. requestBody: content: application/json: schema: type: object examples: jsonObject: value: {"user_id": 1234} responses: 200: description: returns list of users and clubs content: application/json: examples: success: value: {"users":[{"user_id":12345,"name":"John Smith","photo_url":"https://clubhouseprod.s3.amazonaws.com:443/__thumbnail_250x250","username":"johnsmith","bio":"Bio","twitter":null}],"clubs":[{"club_id":123,"name":"Example Club","description":"Example","photo_url":"https://clubhouseprod.s3.amazonaws.com:443/club___thumbnail_250x250","num_members":123,"num_followers":456,"is_member":false,"is_follower":false}],"count":2,"next":null,"previous":null,"success":true} /get_suggested_follows_friends_only: post: summary: find people to follow by uploading contacts during signup requestBody: content: application/json: schema: type: object examples: noContacts: value: {"club_id":null,"upload_contacts":true,"contacts":[]} responses: 200: description: returns list of users and clubs content: application/json: examples: noContacts: value: {"users":[],"success":true} /get_suggested_follows_all: get: summary: gets suggested follows during signup # ?in_onboarding=true&page_size=50&page=1 # ?in_onboarding=false&page_size=25&page=1 parameters: - in: query name: in_onboarding schema: type: boolean example: true - in: query name: page_size schema: type: integer example: 50 - in: query name: page schema: type: integer example: 1 responses: 200: description: a list of users to follow. bios truncated to 80 chars. content: application/json: examples: usersFromOnboard: value: { "users": [ { "user_id": 1234, "name": "John Smith", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/__thumbnail_250x250", "username": "rohan", "bio": "Example bio", "twitter": "johnsmith" } ], "count": 59, "next": 2, "previous": null, "success": true } /update_notifications: post: summary: updates notification during signup. requestBody: content: application/json: schema: type: object examples: onboardingSetting: value: {"enable_trending":-1,"pause_till":-1,"is_sandbox":false,"apn_token":null,"system_enabled":2,"frequency":-1} responses: 400: description: waitlisted content: application/json: examples: waitlisted: value: {"success":false,"error_message":"Your account is waitlisted. Please contact us if this is in error."} /get_online_friends: post: summary: gets online friends on the app homepage. requestBody: content: application/json: schema: type: object examples: emptyRequest: value: {} responses: 200: description: a list of online clubs and users. content: application/json: examples: emptyOnlineFriends: value: { "clubs": [], "users": [] } /get_events: get: summary: the Upcoming for You page parameters: - in: query name: is_filtered schema: type: boolean example: true - in: query name: page_size schema: type: integer example: 25 - in: query name: page schema: type: integer example: 1 responses: 200: description: a list of events content: application/json: examples: emptyEventsList: value: { "events": [], "count": 0, "next": null, "previous": null, "success": true } /get_settings: get: summary: get notification settings responses: 200: description: notification settings content: application/json: examples: defaultSettings: value: { "notifications_enable_trending": true, "notifications_frequency": 3, "notifications_is_paused": false, "success": true } /get_welcome_channel: get: summary: called during signup responses: 200: description: empty??? content: application/json: examples: defaultSettings: value: { "is_empty": true, "success": true } /record_action_trails: post: summary: analytics requestBody: content: application/json: schema: type: object examples: appOpened: value: {"action_trails":[{"blob_data":{},"trail_type":"app_opened","client_time_created":1612971962}]} responses: 200: description: always empty content: application/json: examples: success: value: {"success": true} /get_notifications: get: summary: get notifications (the bell icon) parameters: - in: query name: page_size schema: type: integer example: 20 - in: query name: page schema: type: integer example: 1 responses: 200: description: Returns a list of notifications. content: application/json: examples: empty: value: {"notifications":[],"disabled":false,"count":0,"next":null,"previous":null,"success":true} /get_actionable_notifications: get: summary: get actionable notifications (the bell again) responses: 200: description: Returns a list of actionable notifications. content: application/json: examples: empty: value: {"notifications":[],"count":0,"next":null,"previous":null,"success":true} /get_create_channel_targets: post: summary: is fetched when you tap Create Room requestBody: content: application/json: schema: type: object example: {} responses: 400: description: waitlisted content: application/json: examples: waitlisted: value: {"success":false,"error_message":"Your account is waitlisted. Please contact us if this is in error."} /create_channel: post: summary: creates a channel requestBody: content: application/json: schema: type: object examples: openNoTopic: value: {"is_social_mode":false,"club_id":null,"user_ids":[],"is_private":false,"event_id":null,"topic":null} socialWithTopic: value: {"is_social_mode":true,"club_id":null,"user_ids":[],"is_private":false,"event_id":null,"topic":"topic"} # TODO(zhuowei): private responses: 400: description: waitlisted content: application/json: examples: waitlisted: value: {"success":false,"error_message":"Your account is waitlisted. Please contact us if this is in error."} /get_suggested_speakers: post: summary: gets suggested users when you start a private room requestBody: content: application/json: schema: type: object examples: startingPrivateRoom: value: {"channel":null} responses: 200: description: Returns a list of actionable notifications. content: application/json: examples: empty: value: {"users":[],"count":0,"next":null,"previous":null,"clubs":[],"success":true} /search_users: post: summary: search for users requestBody: content: application/json: schema: type: object examples: normalSearch: # from the top Search button in Clubhouse value: { "cofollows_only": false, "following_only": false, "followers_only": false, "query": "johnsmith" } responses: 200: description: Returns a list of users. content: application/json: examples: successful: # bios not truncated value: { "users": [ { "user_id": "1234", "name": "John Smith", "username": "johnsmith", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/__thumbnail_250x250", "bio": "Bio!" } ], "count": 1, "next": null, "previous": null, "success": true } /get_suggested_follows_similar: post: summary: find similar users. (The Sparkles button on Clubhouse's profile page) requestBody: content: application/json: schema: type: object example: {"user_id":4} responses: 200: description: Returns a list of users. Bios truncated to 80 chars. content: application/json: examples: successful: value: { "users": [ { "user_id": "1234", "name": "John Smith", "username": "johnsmith", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/__thumbnail_250x250", "bio": "", "twitter": null } ], "success": true } /search_clubs: post: summary: search clubs. requestBody: content: application/json: schema: type: object examples: normalSearch: # from the top Search button in Clubhouse value: {"cofollows_only":false,"following_only":false,"followers_only":false,"query":"rohan"} responses: 200: description: Returns a list of clubs. content: application/json: examples: successful: value: { "clubs": [ { "club_id": "1234", "name": "Example club", "description": "Example club", "photo_url": "https://clubhouseprod.s3.amazonaws.com:443/club___thumbnail_250x250", "num_followers": 1234, "num_members": 5678, "is_member": false, "is_follower": false }, ], "count": 1, "next": null, "previous": null, "success": true }