Skip to content

Authentication

Meetup uses access tokens to authenticate GraphQL requests. If you do not have a token or if your token is expired, please go to OAuth2 Server Flow to create or manage your tokens.

To authenticate the requests, set the Authorization HTTP header in your requests.

HeaderValue
AuthorizationBearer {YOUR_TOKEN}

To verify that your token is working, run the following command in the terminal:

query='query { self { id name } }'
curl -X POST https://api.meetup.com/gql-ext \
  -H 'Authorization: Bearer {YOUR_TOKEN}' \
  -H 'Content-Type: application/json' \
  -d @- <<EOF
    {"query": "$query"}
EOF

You should see a response similar to the example below if your token works correctly. If not, please go to OAuth2 Server Flow to create or refresh your token.

{
  "data":{
    "self":{
      "id":1234,
      "name":"Cool Developer"
    }
  }
}

Querying with GraphQL

GraphQL queries return JSON with only the fields you specify. For example, the following invokes the event query with the eventId as an argument. The query returns the title, description, and date from an event.

Example: Basic query

Query

query {
  event(id: "276754274") {
    title
    description
    dateTime
  }
}

Response

{
  "data": {
    "event": {
      "title": "API test",
      "description": "API test description",
      "dateTime": "2021-03-15T09:52:53+09:00"
    }
  }
}

Example: Variables

Variables simplify GraphQL queries and mutations by letting you pass data like you do for arguments to a function. Variables begin with $ and are defined in JSON format.

Basic example with variables

query ($eventId: ID) {
  event(id: $eventId) {
    title
    description
    dateTime
  }
}

Variables

{ "eventId": "276754274" }

Equivalent curl command

query='query($eventId: ID!) {
  event(id: $eventId) {
    title
    description
    dateTime
  }
}
'
variables='{
  "eventId": "276754274"
}'
query="$(echo $query)"
curl -X POST https://api.meetup.com/gql-ext \
  -H 'Authorization: Bearer {YOUR_TOKEN}' \
  -H 'Content-Type: application/json' \
  -d @- <<EOF
      {"query": "$query", "variables": "$variables"}
EOF

Example: Pagination

Retrieving large collections of data takes a long time and is resource intensive. Because of this, we recommend using pagination. Pagination is the process of dividing the records in results into smaller sets or pages. Meetup GraphQL uses cursor-based pagination.

Cursor-based pagination works by returning an identifier or cursor to a specific record in the dataset. Meetup GraphQL uses the endCursor field in the pageInfo object as the reference to the last record in the page. On subsequent requests, passing along the endCursor value and size as arguments tells GraphQL to return results after the given cursor.

First, let’s get the first 3 groups of Pro Network and get the endCursor of the pageInfo object. The default page size is 20, so to get the first 3 you need to specify it in the first argument.

Query

query ($urlname: ID) {
  proNetwork(urlname: $urlname) {
    groupsSearch(input: {first: 3}) {
      totalCount
      pageInfo {
        endCursor
      }
      edges {
        node {
          id
          name
        }
      }
    }
  }
}

Variables

{ "urlname": "YOUR_NETWORK_URLNAME" }

Response

{
  "data": {
    "proNetwork": {
      "groupsSearch": {
        "totalCount": 15,
        "pageInfo": {
          "endCursor": "[['1524147138299'],['28214336']]"
        },
        "edges": [
          {
            "node": {
              "id": "33662217",
              "name": "Kharkiv Adventure Meetup Group"
            }
          },
          {
            "node": {
              "id": "29968188",
              "name": "L0t$ of $pecial Ch@r@cters *.* đź‘»"
            }
          },
          {
            "node": {
              "id": "28214336",
              "name": "Testing it out"
            }
          }
        ]
      }
    }
  }
}

To fetch the next set of records, copy the endCursor value and use it as after the argument value. The following example includes both fields and uses query variables to pass their values as arguments. The $itemsNum variable is required, and is used to specify the number of results to return.

Query

query ($urlname: ID!, $itemsNum: Int!, $cursor: String!) {
  proNetwork(urlname: $urlname) {
    groupsSearch(input: {first: $itemsNum, after: $cursor}) {
      totalCount
      pageInfo {
        endCursor
      }
      edges {
        node {
          id
          name
        }
      }
    }
  }
}

Variables

{
  "urlname": "YOUR_NETWORK_URLNAME",
  "itemsNum": 3,
  "cursor": "[['1524147138299'],['28214336']]"
}

Example: Collecting a list of groups in your network

To get a list of all groups from your network, first you need to get a Pro Network by ID or by urlname. Then, use the groupsSearch subquery.

Query

query($urlname: ID!) {
  proNetwork(urlname: $urlname) {
    groupsSearch(input: { first: 2, filter: {} }) {
      totalCount
      pageInfo {
        endCursor
      }
      edges {
        node {
          id
          name
          urlname
          memberships {
            totalCount
          }
        }
      }
    }
  }
}

Variables

{ "urlname": "YOUR_NETWORK_URLNAME" }

Response

{
  "data": {
    "proNetwork": {
      "groupsSearch": {
        "totalCount": 15,
        "pageInfo": {
          "endCursor": "[['1524147138299'],['28214336']]"
        },
        "edges": [
          {
            "node": {
              "id": "33662217",
              "name": "Kharkiv Adventure Meetup Group",
              "urlname": "Kharkiv-Adventure-Meetup-Group",
              "memberships": {
                "totalCount": 4
              }
            }
          },
          {
            "node": {
              "id": "28214336",
              "name": "Testing it out",
              "urlname": "meetup-pro-test-group",
              "memberships": {
                "totalCount": 34
              }
            }
          }
        ]
      }
    }
  }

In addition, you can search groups by different filter params which can be added to the input object

Example: Collecting list of upcoming events from the Pro Network

You can filter for upcoming events by calling the eventsSearch subquery and setting the status filter.

Query

query($urlname: ID!) {
  proNetwork(urlname: $urlname) {
    eventsSearch(input: { first: 3, filter: { status: "UPCOMING" } }) {
      totalCount
      pageInfo {
        endCursor
      }
      edges {
        node {
          id
          title
        }
      }
    }
  }
}

Variables

{ "urlname": "YOUR_NETWORK_URLNAME" }

Response

{
  "data": {
    "proNetwork": {
      "eventsSearch": {
        "totalCount": 15,
        "pageInfo": {
          "endCursor": "[['1524147138299'],['28214336']]"
        },
        "edges": [
          {
            "node": {
              "id": "33662217",
              "title": "Kharkiv Adventure Meetup"
            }
          },
          {
            "node": {
              "id": "28214336",
              "title": "Testing it out"
            }
          }
        ]
      }
    }
  }
}

In addition, you can search groups by different filter params which can be added to the input object

Example: Collecting event information

Use the event query and filter to fetch Event details.

Query

query($eventId: ID!) {
  event(id: $eventId) {
    title
    eventUrl
    description
    dateTime
    duration
    eventHosts {
      memberId
      name
    }
    featuredEventPhoto {
      id
      baseUrl
    }
    group {
      id
      name
      urlname
    }
    rsvps {
      edges {
        node {
          id
          member {
            name
          }
        }
      }
    }
  }
}

Variables

{
  "$eventId": "EVENT_ID"
}

Response

{
  "data": {
    "event": {
      "title": "Testing API things 1",
      "eventUrl": "https://www.meetup.com/whiskey-wednesdays/events/305912103/",
      "description": "",
      "dateTime": "2025-02-19T19:00:00-07:00",
      "duration": "PT0S",
      "eventHosts": [
        {
          "memberId": "246752233",
          "name": "Test"
        }
      ],
      "featuredEventPhoto": {
        "id": "495693322",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/"
      },
      "group": {
        "id": "6622782",
        "name": "Whiskey Wednesdays",
        "urlname": "whiskey-wednesdays"
      },
      "rsvps": {
        "edges": [
          {
            "node": {
              "id": "1870061534!chp",
              "member": {
                "name": "Vadim Vlasenko (test)"
              }
            }
          }
        ]
      }
    }
  }
}

In addition, you can get other fields from the Event object

Example: Collecting RSVP information

To fetch RSVP information for upcoming events, you first call the eventSearch query to get the list of upcoming events. Then, retrieve their RSVP details.

Query

query($urlname: ID!) {
  proNetwork(urlname: $urlname) {
    eventsSearch(input: { first: 3, filter: { status: "UPCOMING" } }) {
      totalCount
      pageInfo {
        endCursor
      }
      edges {
        node {
          id
          rsvps {
            edges {
              node {
                id
                member {
                  name
                  email
                }
              }
            }
          }
        }
      }
    }
  }
}

Variables

{
	"urlname": "YOUR_NETWORK_URLNAME"
}

Response

{
  "data": {
    "proNetwork": {
      "eventsSearch": {
        "totalCount": 12,
        "pageInfo": {
          "endCursor": "[['1654383600000'],['264247573']]"
        },
        "edges": [
          {
            "node": {
              "id": "278461755",
              "rsvps": {
                "edges": [
                  {
                    "node": {
                      "id": "1874780639!chp",
                      "member": {
                        "name": "test test (test)",
                        "email": "email@meetup.com"
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "id": "278450119",
              "rsvps": {
                "edges": [
                  {
                    "node": {
                      "id": "1874730763!chp",
                      "member": {
                        "name": "test test",
                        "email": "email@meetup.org"
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "id": "278000838",
              "rsvps": {
                "edges": [
                  {
                    "node": {
                      "id": "1872396589!chp",
                      "member": {
                        "name": "test test",
                        "email": "email@meetup.com"
                      }
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}

You can check other fields from the Rsvp object if you need

Example: Collecting RSVP registration answers

RSVP registration answers for a given event can be retrieved by using the eventRegistrationAnswers query.

Query

query($urlname: ID!, $eventId: ID!) {
  proNetwork(urlname: $urlname) {
    eventRegistrationAnswers(input: { filter: { eventIds: [$eventId]}}) {
      totalCount
      edges {
        node {
          answers {
            question
            answer
          }
        }
      }
    }
  }
}

Variables

{
	"urlname": "YOUR_NETWORK_URLNAME"
	"$eventId": "EVENT_ID"
}

Response

{
  "data": {
    "proNetwork": {
      "eventRegistrationAnswers": {
        "totalCount": 1,
        "edges": [
          "answers": [
            {
              "question": "Some question",
              "answer": "Some answer"
            },
            {
              "question": "Another question",
              "answer": "Another answer"
            }
          ]
        ]
      }
    }
  }
}

Publishing with GraphQL

While GraphQL queries are used to fetch data, mutations modify records. They can be used to insert, update, or delete data.

Example: Publishing a new event

Events are typically drafted and reviewed before being published for the community to see. This example shows how to create a draft and publish the event. Please check the input schema for more detailed information.

Mutation

mutation($input: CreateEventInput!) {
  createEvent(input: $input) {
    event {
      id
    }
    errors {
      message
      code
      field
    }
  }
}

Variables

{
	"input": {
		"groupUrlname": "GROUP_URLNAME",
		"title": "EVENT_TITLE",
		"description": "EVENT_DESCRIPTION",
		"startDateTime": "EVENT_STARTTIME",
		"venueId": "EVENT_VENUE_ID",
		"duration": "EVENT_DURATION",
		"publishStatus": "DRAFT"
	}
}

Response

{
  "data": {
    "createEvent": {
      "event": {
        "id": "278472065"
      },
      "errors": []
    }
  }
}

Once you have the event drafted and ready to be published, run the following mutation

Mutation

mutation($input: EditEventInput!) {
  editEvent(input: $input) {
    event {
      id
    }
    errors {
      message
      code
      field
    }
  }
}

Variables

{
	"input": {
		"eventId": "EVENT_ID",
		"publishStatus": "PUBLISHED"
	}
}

Response

{
  "data": {
    "editEvent": {
      "event": {
        "id": "278472065"
      },
      "errors": []
    }
  }
}

Example: Uploading an image

Uploading images consists of 2 parts. Creating a placeholder with a file name and then uploading the binary to our system.

First, create a placeholder for the image in the system by invoking the createGroupEventPhoto mutation.

Mutation

mutation($input: GroupEventPhotoCreateInput!) {
  createGroupEventPhoto(input: $input) {
    uploadUrl
    photo {
      id
      baseUrl
    }
    imagePath
  }
}

Variables

{
	"input": {
		"groupId": "GROUP_ID",
		"photoType": "GROUP_PHOTO",
		"contentType": "JPEG",
		"setAsMain": false
	}
}

Response

{
  "data": {
    "uploadImage": {
      "uploadUrl": "{upload link}",
      "photo": {
        "id": "496557439",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
      },
      "imagePath": ""
    }
  }
}

The response contains an uploadUrl where you can upload the image content-type using Postman, curl, or other similar application. The ID value can be used as reference to other queries or in other mutations such as modifying an event.

Example: Editing an event

Please check the input object description for other fields which can be edited.

Mutation

mutation($input: EditEventInput!) {
  editEvent(input: $input) {
    event {
      id
    }
    errors {
      message
      code
      field
    }
  }
}

Variables

{
	"input": {
		"eventId": "EVENT_ID",
		"description": "EVENT_DESCRIPTION",
		"howToFindUs": ”EVENT_LOCATION_HINT”,
		"featuredPhotoId": PHOTO_ID
	}
}

Response

{
  "data": {
    "editEvent": {
      "event": {
        "id": "278472065"
      },
      "errors": []
    }
  }
}

Working with Photos

The Meetup API provides access to the various photos associated with members, events, and groups. You can access these photos with the PhotoInfo object. The PhotoInfo object provides you with the flexibility to choose the size and type of image you would like to display. We currently support JPG and WEBP image formats.

Example: Event photo

Query

query($eventId: ID!) {
  event(id: $eventId) {
    id
    featuredEventPhoto {
      id
      baseUrl
    }
  }
}

Variables

{ "eventId": "283013941" }

Response

{
  "data": {
    "event": {
      "id": "283013941",
      "featuredEventPhoto": {
        "id": "501175080",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/"
      }
    }
  }
}

In the above example, you can access the featured event photo as the first image in the images array. You will receive a list of PhotoInfo objects in the array. Then you can concatenate the baseUrl and Id of any given image with the desired width, height, and type of the image you desire.

As an example, here are two variations of valid photo links from the above response:

Rate Limiting

The Meetup API aims to provide consistent responsiveness and equal quality of service for all its consumers. In order to do so, we limit the frequency at which the API will produce successful responses to a single client.

The API currently allows you to have 500 points in your queries every 60 seconds. Clients that issue too many requests in a short period of time will receive an error message in their response. The error will display which query has caused you to be rate-limited along with the time when your limit will be reset.

{
  "errors": [
    {
      "message": "Too many requests, please try again shortly.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "event"
      ],
      "extensions": {
        "code": "RATE_LIMITED",
        "consumedPoints": 500,
        "resetAt": "2021-12-12T18:37:51.644Z"
      }
    }
  ],
  "data": null
}

This response indicates that you are making requests too quickly. If you receive one of these errors, you should adjust the frequency of your requests by adding a pause between them.

Meetup API migration guide

In February 2025, we released a new version of Meetup’s GraphQL API.

With this change, the Meetup API will support full schema introspection, enabling any customer to use any GraphQL tool to explore the schema seamlessly.

However, it’s possible that previous API configurations may break as a result of these changes. This is largely a result of us renaming certain data fields. This guide is intended to help you easily adapt your existing integration to take advantage of the new and improved API and avoid downtime.

We invite you to perform the following steps:

  • 1. Update the API URL to the new one: https://api.meetup.com/gql-ext
  • 2. Validate a query; Since the introspection is enabled for the new API, any query can be easily validated against the new API by using any GraphQL tool.
  • 3. If a query is invalid, identify specific issues and review the major changes listed below.
  • 4. Сontact support if you continue to experience issues.

Release notes

General changes
OldRenamed to / replaced byMoved to
TicketRSVP
UserMember
GroupMembershipMembership
ImagePhoto
keywordSearchSplit into: groupSearch, eventSearch
proNetworkByUrlnameproNetwork
uploadImagecreateGroupEventPhoto
counttotalCount
Event type changes
OldRenamed to / replaced byMoved to
image/imagesfeaturedEventPhoto
isOnlineeventType The Event.type field is much more powerful as it now supports online, inPerson, and hybrid as well.
going, waiting, ticketsrsvps (with filters applied)
attendingTicket, ticketrsvp
Member type changes
OldRenamed to / replaced byMoved to
ticketsrsvps
hostedEvents, upcomingEvents, draftEventsmemberEvents (with filters applied)
recommendedGroupsQuery.recommendedGroups
Membership type changes
OldRenamed to / replaced byMoved to
mostRecentVisitDatelastAccessTime
joinedDatejoinTime
noShowCountRsvpStats.noShowCount
Group type changes
OldRenamed to / replaced byMoved to
logokeyGroupPhoto
latitudelat
longitudelon
topicsactiveTopics
membershipSearchmemberships
draftEvents, pastEvents, upcomingEventsevents (with filters applied)
ProNetwork type changes
OldRenamed to / replaced byMoved to
isMemberEmailSharedisProEmailShared
rsvpSurveyseventRegistrationAnswers
RSVP type changes
OldRenamed to / replaced byMoved to
usermember
quantityRemoved
updatedAtupdated