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 \
  -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 \
  -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: String!) {
  proNetworkByUrlname(urlname: $urlname) {
    groupsSearch(input: {first: 3}) {
      count
      pageInfo {
        endCursor
      }
      edges {
        node {
          id
          name
        }
      }
    }
  }
}

Variables

{ "urlname": "YOUR_NETWORK_URLNAME" }

Response

{
  "data": {
    "proNetworkByUrlname": {
      "groupsSearch": {
        "count": 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: String!, $itemsNum: Int!, $cursor: String!) {
  proNetworkByUrlname(urlname: $urlname) {
    groupsSearch(input: {first: $itemsNum, after: $cursor}) {
      count
      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: String!) {
  proNetworkByUrlname(urlname: $urlname) {
    groupsSearch(filter: {}, input: { first: 2 }) {
      count
      pageInfo {
        endCursor
      }
      edges {
        node {
          id
          name
          urlname
          memberships {
            count
          }
        }
      }
    }
  }
}

Variables

{ "urlname": "YOUR_NETWORK_URLNAME" }

Response

{
  "data": {
    "proNetworkByUrlname": {
      "groupsSearch": {
        "count": 15,
        "pageInfo": {
          "endCursor": "[['1524147138299'],['28214336']]"
        },
        "edges": [
          {
            "node": {
              "id": "33662217",
              "name": "Kharkiv Adventure Meetup Group",
              "urlname": "Kharkiv-Adventure-Meetup-Group",
              "memberships": {
                "count": 4
              }
            }
          },
          {
            "node": {
              "id": "28214336",
              "name": "Testing it out",
              "urlname": "meetup-pro-test-group",
              "memberships": {
                "count": 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: String!) {
  proNetworkByUrlname(urlname: $urlname) {
    eventsSearch(filter: { status: UPCOMING }, input: { first: 3 }) {
      count
      pageInfo {
        endCursor
      }
      edges {
        node {
          id
          title
          dateTime
        }
      }
    }
  }

Variables

{ "urlname": "YOUR_NETWORK_URLNAME" }

Response

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

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
    host {
      id
      name
    }
    images {
      id
      baseUrl
      preview
    }
    group {
      id
      name
      urlname
    }
    tickets {
      edges {
        node {
          id
          user {
            name
          }
          createdAt
        }
      }
    }
  }
}

Variables

{
  "$eventId": "EVENT_ID"
}

Response

{
  "data": {
    "event": {
      "title": "Nastya's network event (test) 2",
      "eventUrl": "https://beta2.dev.meetup.com:8000/Whiskey-Wednesdays/events/277543160",
      "description": "let's test it",
      "dateTime": "2023-01-31T15:00-07:00",
      "duration": "PT1H",
      "host": {
        "id": "240771416",
        "name": "Vadim Vlasenko (test)"
      },
      "images": [
        {
          "id": "495693322",
          "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
          "preview": null
        }
      ],
      "group": {
        "id": "6622782",
        "name": "Whiskey Wednesdays 3",
        "urlname": "Whiskey-Wednesdays"
      },
      "tickets": {
        "edges": [
          {
            "node": {
              "id": "1870061534!chp",
              "user": {
                "name": "Vadim Vlasenko (test)"
              },
              "createdAt": "2021-05-31T13:11:34.522+03:00[Europe/Moscow]"
            }
          }
        ]
      }
    }
  }
}

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: String!) {
  proNetworkByUrlname(urlname: $urlname) {
    eventsSearch(filter: { status: UPCOMING }, input: { first: 3 }) {
      count
      pageInfo {
        endCursor
      }
      edges {
        node {
          id
          tickets {
            edges {
              node {
                id
                user {
                  name
                  email
                }
                createdAt
              }
            }
          }
        }
      }
    }
  }
}

Variables

{
	"urlname": "YOUR_NETWORK_URLNAME"
}

Response

{
  "data": {
    "proNetworkByUrlname": {
      "eventsSearch": {
        "count": 12,
        "pageInfo": {
          "endCursor": "[['1654383600000'],['264247573']]"
        },
        "edges": [
          {
            "node": {
              "id": "278461755!chp",
              "tickets": {
                "edges": [
                  {
                    "node": {
                      "id": "1874780639!chp",
                      "user": {
                        "name": "test test (test)",
                        "email": "email@meetup.com"
                      },
                      "createdAt": "2021-05-28T15:25:02.910Z[UTC]"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "id": "278450119!chp",
              "tickets": {
                "edges": [
                  {
                    "node": {
                      "id": "1874730763!chp",
                      "user": {
                        "name": "test test",
                        "email": "email@meetup.org"
                      },
                      "createdAt": "2021-05-28T15:25:02.900Z[UTC]"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "id": "278000838!chp",
              "tickets": {
                "edges": [
                  {
                    "node": {
                      "id": "1872396589!chp",
                      "user": {
                        "name": "test test",
                        "email": "email@meetup.com"
                      },
                      "createdAt": "2021-05-28T15:25:02.901Z[UTC]"
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}

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

Example: Collecting RSVP registration answers

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

Query

query($urlname: String!, $eventId: ID!) {
  proNetworkByUrlname(urlname: $urlname) {
    rsvpSurveys(filter: { events: [$eventId]}) {
      count
      edges {
        node {
          id
          answers {
            question
            answer
          }
        }
      }
    }
  }
}

Variables

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

Response

{
  "data": {
    "proNetworkByUrlname": {
      "rsvpSurveys": {
        "count": 1,
        "edges": [
          "id": "2780087650838",
          "answers": [
            {
              "question": "Some question",
              "answer": "Some answer"
            },
            {
              "question": "Another question",
              "answer": "Another answer"
            }
          ]
        ]
      }
    }
  }
}

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

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!chp"
      },
      "errors": []
    }
  }
}

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

Mutation

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

Variables

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

Response

{
  "data": {
    "editEvent": {
      "event": {
        "id": "278472065!chp"
      },
      "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 uploadImage mutation.

Mutation

mutation($input: ImageUploadInput!) {
  uploadImage(input: $input) {
    uploadUrl
    image {
      id
      baseUrl
      preview
    }
    imagePath
  }
}

Variables

{
	"input": {
		"groupId": GROUP_ID,
		"photoType": GROUP_PHOTO,
		"fileName": IMAGE_FILENAME,
		"contentType": JPEG
	}
}

Response

{
  "data": {
    "uploadImage": {
      "uploadUrl": "{upload link}",
      "image": {
        "id": "496557439",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      },
      "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!chp"
      },
      "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 Image object. The Image 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
    images {
      id
      baseUrl
    }
  }
}

Variables

{ "eventId": "283013941" }

Response

{
  "data": {
    "event": {
      "id": "283013941!chp",
      "images": [
        {
          "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 Image 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.