Skip to content

Latest commit

 

History

History
157 lines (116 loc) · 5.7 KB

File metadata and controls

157 lines (116 loc) · 5.7 KB

Data reference

public/events.json is the whole product in one file. It's served CORS-open with no key and no rate limit — build on it freely.

curl https://hackathons.dev/events.json

Regenerated by GitHub Actions four times a day (00:00, 06:00, 12:00, 18:00 UTC). Check generatedAt rather than assuming freshness.


Top level

Field Type Notes
generatedAt ISO 8601 string When the ingest ran
count number Always equals events.length
seeded boolean? Present and true only for fixture-seeded local builds
sources Source[] Per-source health for this run
events Event[] Sorted: open first by closest deadline, then upcoming

Source

Field Type Notes
id string devpost, hackclub, mlh
name string Display name
url string The platform's own listing page
count number Events contributed this run
ok boolean false means the source failed and its events are missing

Always check ok. A source can fail without the file failing — that's by design, but it means coverage is silently incomplete for that run.


Event

Identity

Field Type Notes
id string Globally unique, {source}-{sourceId}. Stable across runs.
source string Source id
sourceName string Source display name
alsoOn string[] Other sources carrying the same event (see Deduplication)

Core

Field Type Notes
title string Never empty
url string Always https://. Links to the organizer, not to us.
imageUrl string | null null when absent or a known placeholder
organizer string | null
status "open" | "upcoming" | "ended" ended events are filtered out before publishing

Timing

Field Type Notes
startsAt ISO 8601 | null
endsAt ISO 8601 | null Submission deadline. The primary sort key.
deadlineLabel string | null Source's own phrasing, e.g. "24 days left"
dateLabel string | null Human range, e.g. "May 19 - Aug 17, 2026"

Dates are best-effort. Devpost publishes only display strings, so scripts/lib/parse.mjs reconstructs ISO timestamps from them. Roughly 100% parse cleanly today, but treat null as genuinely unknown rather than assuming a default. Times default to 23:59 UTC when the source gives no time component.

Location

Field Type Notes
format "online" | "in-person" | "hybrid"
locationLabel string | null "Online", or a venue/city string
city, country, countryCode string | null Populated mainly by Hack Club
lat, lng number | null Hack Club only — pre-geocoded, ready for map views

Prizes

Field Type Notes
prizeUsd number | null Approximate USD. For sorting and filtering only.
prizeLabel string | null Original currency and amount, e.g. "₹3,000". Display this.
prizeCurrency string | null ISO 4217, e.g. USD, INR, CAD
cashPrizeCount number Number of distinct cash prizes

prizeUsd uses fixed conversion rates that are refreshed rarely. It exists to make prizes comparable across currencies — never present it as an exact conversion. prizeLabel is null when the prize pool is zero.

Eligibility and classification

Field Type Notes
themes string[] Source-provided tags, e.g. "Machine Learning/AI". Empty for MLH and Hack Club.
audience "high-school" | "university" | "open" Inferred; see below
inviteOnly boolean Filter these out for general audiences
beginnerFriendly boolean From an explicit tag, or true for high-school events
participants number | null Registration count. Devpost only.

audience is inferred, not authoritative. Hack Club events are always high-school and MLH events default to university, both of which are reliable. Devpost events are classified by keyword matching on title and organizer, which is a heuristic — verify on the listing page before relying on it.


Deduplication

The same hackathon regularly appears on several platforms — MLH-sanctioned events often have a Devpost page too. Events are matched on a normalized title (stripped of years and filler words like "hackathon") plus start day.

When a match is found the richer record wins — SOURCE_RANK in scripts/ingest.mjs prefers Devpost, since it carries prize and participant data the others don't. The losing source is preserved in alsoOn rather than dropped, so nothing is silently hidden.

This means alsoOn: ["Major League Hacking"] tells you the event is MLH-sanctioned even though the record came from Devpost.


Quality guarantees

scripts/validate.mjs runs before anything is published and blocks the commit on:

  • any required field missing
  • duplicate id
  • non-HTTPS url
  • invalid format
  • a deadline more than 24 hours in the past
  • a drop of more than 50% in event count versus the last published run

That last one is the important one. A sudden cliff almost always means a source silently changed its API shape, and stale-but-correct data beats fresh-but-broken data every time.

It warns (without blocking) when deadline coverage falls below 80%, when more than 60% of events lack images, or when any source failed.


Stability

The schema is additive — new fields may appear, existing ones won't change type or disappear without a note here. id values are stable across runs, so they're safe to store.

If you build something on this, an issue or a link back is welcome.