{"openapi":"3.2.0","info":{"title":"Hack Club Events API","version":"1.0.0","summary":"The official public API for all Hack Club events.","description":"In the past we used to have issues where we would have many different APIs claiming to be the source of truth for Hack Club events, however each would be somehow outdated or incomplete. This API is now supposed to be the single source of truth. It fetches directly from our internal Airtables (specifically the Unified YSWS table) and is the only API that is guaranteed to be most up to date.\n\nDue to Airtable ratelimits, we cache for 5 minutes maximum. Any image URLs signed by Airtable only last for about 2 hours, so don't hotlink them, but cache them heavily or just store the raw image. These are unlikely to change frequently.\n\nEvery error is JSON with a stable `code`, a `message`, a `hint`, and the HTTP `status` repeated in the body for your convenience. This API and documentation is made with AI agents in mind, so point them here if you want them to understand the API. Pages on this site answer `Accept: text/markdown`; see /llms.txt for the page index.\n\nWhile the site does serve some other API endpoints, these are deliberately undocumented and should not be built on as they only serve internal purposes. The only public API is under `/api/v1/events`.\n\nVersioning and deprecation policy: https://ru.hackclub.com/api/versioning. Breaking changes require a new major URL version. Hack Club normally gives at least 90 days' notice before removing a documented endpoint. No documented v1 endpoint is currently deprecated or scheduled for removal.","contact":{"name":"Hack Club","email":"echo@hackclub.com"},"x-versioning-policy":"https://ru.hackclub.com/api/versioning","license":{"name":"MIT","identifier":"MIT"}},"servers":[{"url":"https://ru.hackclub.com","description":"This deployment"}],"security":[{}],"externalDocs":{"description":"Rendered API reference","url":"https://ru.hackclub.com/api/v1/docs"},"tags":[{"name":"events","description":"Hack Club events"}],"paths":{"/api/v1/events":{"get":{"tags":["events"],"operationId":"listEvents","summary":"List events","description":"Every Hack Club event, with the styling its card uses on the site.","parameters":[{"name":"status","in":"query","required":false,"description":"Only events in these states. Repeat the parameter or pass a comma-separated list.","schema":{"type":"array","items":{"type":"string","enum":["upcoming","ongoing","ended"]}},"explode":true},{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"type":"string","enum":["startDate","announcedAt","name"],"default":"startDate"},"explode":false},{"name":"order","in":"query","required":false,"description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"],"default":"desc"},"explode":false},{"name":"limit","in":"query","required":false,"description":"Maximum number of events to return, 1–100.","schema":{"type":"integer","minimum":1,"maximum":100},"explode":false}],"responses":{"200":{"description":"Events.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"}}}},"400":{"description":"A query parameter was not understood.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The server is missing its Airtable credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The upstream Airtable request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/events/{idOrSlug}":{"get":{"tags":["events"],"operationId":"getEvent","summary":"Get one event","description":"Look an event up by its `id` or its `slug`. Prefer the `id`: a slug is derived from the name and changes if the event is renamed for whatever reason","parameters":[{"name":"idOrSlug","in":"path","required":true,"description":"An event's Airtable record id, or its slug.","schema":{"type":"string"}}],"responses":{"200":{"description":"The event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventItem"}}}},"404":{"description":"No event has that id or slug.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The server is missing its Airtable credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The upstream Airtable request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/events/rss":{"get":{"tags":["events"],"operationId":"getEventsFeed","summary":"Newly announced events, as RSS","description":"An RSS 2.0 announcement feed: the 20 most recently added events, newest first, whatever state they are in.","parameters":[{"name":"limit","in":"query","required":false,"description":"Maximum number of events to return, 1–100.","schema":{"type":"integer","minimum":1,"maximum":100},"explode":false}],"responses":{"200":{"description":"An RSS 2.0 feed.","content":{"application/rss+xml":{"schema":{"type":"string"}}}},"400":{"description":"A query parameter was not understood.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The server is missing its Airtable credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The upstream Airtable request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","description":"Structured error body returned by every endpoint under /api.","required":["error","code","message","status","documentation_url"],"properties":{"error":{"type":"string","description":"Human-readable message. Same text as `message`."},"code":{"type":"string","description":"Stable machine-readable identifier for the failure.","enum":["bad_request","unauthorized","forbidden","not_found","method_not_allowed","not_acceptable","unsupported_media_type","payload_too_large","server_misconfigured","internal_error","upstream_error"]},"message":{"type":"string"},"hint":{"type":"string","description":"What the caller can change to make the request succeed."},"status":{"type":"integer","description":"HTTP status code, repeated in the body."},"documentation_url":{"type":"string","format":"uri"}}},"EventBackground":{"type":"object","description":"How the event's card is filled. `null` if the event has no swag yet.","required":["type","color","imageUrl"],"properties":{"type":{"type":"string","enum":["color","image"],"description":"Which of `color` and `imageUrl` the card actually uses."},"color":{"type":"string"},"imageUrl":{"type":["string","null"],"description":"Signed Airtable URLs only last for about 2 hours, so don't hotlink them, but cache them heavily: https://support.airtable.com/articles/9671148410-airtable-attachment-url-behavior"}}},"EventTheme":{"type":"object","description":"The event's card colours. `null` if the event has no swag yet.","required":["text","accent","logoSize","button"],"properties":{"text":{"type":"string"},"accent":{"type":"string"},"logoSize":{"type":"number"},"button":{"type":"object","required":["color","textColor","borderRadius","borderWidth","borderColor"],"properties":{"color":{"type":"string"},"textColor":{"type":"string"},"borderRadius":{"type":"number"},"borderWidth":{"type":"number"},"borderColor":{"type":"string"}}}}},"EventInPerson":{"type":"object","description":"Where and when the event happens in person. `null` if it is online only.","required":["start","end","location"],"properties":{"start":{"type":["string","null"],"format":"date"},"end":{"type":["string","null"],"format":"date"},"location":{"type":["string","null"]}}},"Event":{"type":"object","required":["id","slug","name","status","startDate","announcedAt"],"properties":{"id":{"type":"string","description":"Airtable record id. This identifier is stable, so store this instead of the slug."},"slug":{"type":"string","description":"Derived from the name. This will change if the event is renamed for whatever reason."},"name":{"type":"string"},"description":{"type":["string","null"]},"url":{"type":["string","null"],"description":"The event's own website."},"status":{"type":"string","enum":["upcoming","ongoing","ended"]},"startDate":{"type":"string","format":"date"},"endDate":{"type":["string","null"],"format":"date","description":"`null` means the event runs indefinitely."},"announcedAt":{"type":"string","format":"date-time","description":"When the event was added."},"format":{"type":["string","null"],"enum":["in-person","online","both",null]},"inPerson":{"oneOf":[{"$ref":"#/components/schemas/EventInPerson"},{"type":"null"}]},"projectTypes":{"type":"array","items":{"type":"string","enum":["Electronics","Websites","Software","Gamedev","CAD","Other"]}},"slackChannel":{"type":["string","null"],"description":"Channel name, without the leading `#`."},"slackUrl":{"type":["string","null"]},"requirements":{"type":["string","null"]},"pinned":{"type":"boolean"},"logoUrl":{"type":["string","null"],"description":"Signed Airtable URLs only last for about 2 hours, so don't hotlink them, but cache them heavily: https://support.airtable.com/articles/9671148410-airtable-attachment-url-behavior"},"background":{"oneOf":[{"$ref":"#/components/schemas/EventBackground"},{"type":"null"}]},"theme":{"oneOf":[{"$ref":"#/components/schemas/EventTheme"},{"type":"null"}]}}},"EventList":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"meta":{"type":"object","required":["count","generatedAt"],"properties":{"count":{"type":"integer","description":"Number of events in `data`, after any filtering and `limit`."},"generatedAt":{"type":"string","format":"date-time"}}}}},"EventItem":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Event"},"meta":{"type":"object","required":["generatedAt"],"properties":{"generatedAt":{"type":"string","format":"date-time"}}}}}}}}