Skip to Content
API Overview

API Overview

The Passport API gives you read access to organizations, events, hackathons, bounties, and builder profiles.

Two surfaces

Passport exposes two HTTP surfaces, and they do not serve the same data.

/api/v1/* — the integrator API. Versioned, key-authenticated, paginated, and the one you should build against. Its event endpoints serve the Luma-synced calendar.

/api/* — internal. Unversioned and unauthenticated. It backs Passport’s own front ends, including this website’s homepage. It carries no stability guarantee and can change without notice. It is documented in Endpoints for reference, not for you to depend on.

Its event catalogue is a mix: most events are synced from Luma and carry a luma_url, and those live on Luma rather than having a page on Passport. Events without a luma_url are hosted on Passport itself. If you are linking to an event, follow that field — it is the only reliable signal of where the event actually lives.

Base URL

https://demo.stellarpassport.xyz/api/v1

Getting an API key

Keys are minted per organization from the Passport admin panel under Admin → API Keys. If your organization does not have admin access yet, get in touch and we will set one up.

Keys look like pk_ followed by 48 hexadecimal characters.

Authentication

Send your key as a bearer token on every request:

Authorization: Bearer pk_your_key_here

The one exception is GET /api/v1/organizations/[slug]/page, which is public and needs no key.

A missing or invalid key returns 401:

{ "error": "Invalid or missing API key" }

See Authentication for details.

Pagination

Every v1 list endpoint uses the same cursor pagination.

ParameterDescription
limitResults per page. Default 50, maximum 100.
cursorOpaque cursor. Pass the next_cursor from the previous response.

Responses carry has_more and next_cursor. On the last page next_cursor is null. A cursor looks like 2026-08-20T16:11:49.159Z|68 — treat it as opaque and pass it back unmodified.

{ "hackathons": [ ... ], "has_more": true, "next_cursor": "2026-08-20T16:11:49.159Z|68" }

A non-numeric limit returns 400 {"error": "Invalid limit"} and an unparseable cursor returns 400 {"error": "Invalid cursor"}. A limit above the maximum is clamped rather than rejected.

One exception: GET /api/v1/organizations does not follow this contract. It ignores limit and cursor and reports a total instead of has_more / next_cursor. It is also filtered — see below.

Errors

Errors return a JSON body with an error field and an appropriate status:

{ "error": "Invalid or missing API key" }
StatusMeaning
400Invalid parameter — a bad limit, cursor, or status value
401Missing or invalid API key
404No such resource

Rate limits

There are no published rate limits, and responses currently return no rate-limit headers. Be a good citizen: cache what you can and avoid tight polling loops. If you need high volume, mention it when you request your key.

Last updated on