This document describes the Meetup GraphQL API schema as discovered through testing and introspection in February 2025. The official Meetup API documentation may be incomplete or outdated.
Note: This documents only what we have actually tested. Fields marked "not tested" exist in the schema but have not been verified to work.
POST https://api.meetup.com/gql-ext
Note: The endpoint changed from /gql to /gql-ext sometime before
February 2025. The old endpoint returns 404.
Include an OAuth2 bearer token in the Authorization header:
Authorization: Bearer <access_token>
Content-Type: application/json
Retrieve the authenticated user's information and their group memberships:
query {
self {
id
name
memberships {
edges {
node {
id
name
urlname
timezone
}
metadata {
role
}
}
}
}
}{
"data": {
"self": {
"id": "user123",
"name": "User Name",
"memberships": {
"edges": [
{
"node": {
"id": "group-id",
"name": "Group Name",
"urlname": "group-url-name",
"timezone": "America/New_York"
},
"metadata": {
"role": "ORGANIZER"
}
}
]
}
}
}
}The metadata.role field is an enum. Values we have confirmed through testing:
| Role | Description |
|---|---|
ORGANIZER |
Primary group organizer (tested) |
COORGANIZER |
Co-organizer with event management permissions (tested) |
MEMBER |
Regular group member (tested) |
Other role values may exist (e.g., EVENT_ORGANIZER, ASSISTANT_ORGANIZER)
but have not been verified.
Retrieve past events for a group (this is what we have tested):
query GetGroupEvents($urlname: String!, $first: Int!, $after: String) {
groupByUrlname(urlname: $urlname) {
id
name
urlname
events(first: $first, after: $after, filter: { status: PAST }) {
pageInfo {
endCursor
hasNextPage
}
edges {
node {
id
title
dateTime
venues {
id
name
address
city
state
country
}
}
}
}
}
}Additional fields exist but are not tested: description, endTime,
eventUrl, going, venues.lat, venues.lng, and others. Use introspection
to discover the full schema.
{
"urlname": "your-group-urlname",
"first": 50,
"after": null
}| Status | Description |
|---|---|
PAST |
Events that have already occurred (tested) |
Other status values likely exist (e.g., UPCOMING, CANCELLED, DRAFT) but
have not been tested.
{
"data": {
"groupByUrlname": {
"id": "group-id",
"name": "Group Name",
"urlname": "group-urlname",
"events": {
"pageInfo": {
"endCursor": "cursor-string",
"hasNextPage": true
},
"edges": [
{
"node": {
"id": "event-id",
"title": "Event Title",
"dateTime": "2025-01-15T19:00:00-05:00",
"venues": [
{
"id": "venue-id",
"name": "Venue Name",
"address": "123 Main St",
"city": "New York",
"state": "NY",
"country": "US"
}
]
}
}
]
}
}
}
}These are breaking changes discovered when migrating from older API versions:
| Before | After |
|---|---|
https://api.meetup.com/gql |
https://api.meetup.com/gql-ext |
| Before | After |
|---|---|
memberships(input: {first: N}) |
memberships (no arguments) - tested |
edges.node.isOrganizer: Boolean |
edges.metadata.role: RoleEnum - tested |
Not verified: Whether memberships.totalCount exists (we don't use it).
| Before | After |
|---|---|
pastEvents(input: {...}) |
events(filter: {status: PAST}) |
event.venue (single object) |
event.venues (array) |
The API uses cursor-based pagination following the Relay Connection specification:
- Request with
first: Nto get N items - Check
pageInfo.hasNextPagein response - If true, use
pageInfo.endCursoras theafterparameter in next request - Repeat until
hasNextPageis false
Example pagination loop:
all_events = []
cursor = None
while True:
result = query_events(urlname=group, first=50, after=cursor)
events = result["groupByUrlname"]["events"]
for edge in events["edges"]:
all_events.append(edge["node"])
if not events["pageInfo"]["hasNextPage"]:
break
cursor = events["pageInfo"]["endCursor"]Past events are returned in chronological order (oldest first), not reverse chronological. When filtering by date range, iterate through all pages and filter client-side rather than assuming you can stop early.
GraphQL errors are returned with HTTP 200 status but include an errors array:
{
"errors": [
{
"message": "Error description",
"extensions": {
"code": "ERROR_CODE"
}
}
],
"data": null
}| Code | Description |
|---|---|
RATE_LIMITED |
Too many requests; includes resetAt timestamp (in code, not tested live) |
Other error codes likely exist but have not been encountered in testing.
To discover the full schema, use a GraphQL introspection query:
query IntrospectionQuery {
__schema {
types {
name
fields {
name
type {
name
kind
ofType {
name
kind
}
}
args {
name
type {
name
kind
}
}
}
}
}
}Or for a specific type:
query TypeQuery {
__type(name: "Group") {
name
fields {
name
type {
name
kind
}
}
}
}- Meetup API Documentation (may be outdated)
- GraphQL Specification
- Relay Connection Specification