ThemeParks.wiki API
A free REST API for live theme park wait times, ride status, showtimes, opening hours and years of recorded history, covering hundreds of parks worldwide.
What the ThemeParks.wiki API does
The ThemeParks.wiki API returns live theme park wait times, ride status, showtimes, restaurant availability, opening hours and historical data for hundreds of parks worldwide. Responses are JSON. The base URL is https://api.themeparks.wiki.
The API is free to use, including in commercial projects. Live data, opening hours and the park directory need no API key. If your product shows the data to its users, display a visible "Powered by ThemeParks.wiki" credit. Paid plans remove that requirement. The Terms & Acceptable Use set out what is allowed: you may build any product on the data, but you may not pass it on as data for other software to consume.
Live wait times
One request returns every ride, show and restaurant in a park, with its current wait time and status.
curl https://api.themeparks.wiki/v1/entity/75ea578a-adc8-4116-a54d-dccb60765ef9/live
That example is Magic Kingdom Park at Walt Disney World Resort. Replace the id with any park, resort or single attraction.
statusis the entity's current state:OPERATING,DOWN,CLOSEDorREFURBISHMENT.queue.STANDBY.waitTimeis the posted standby wait in minutes.queue.SINGLE_RIDERandqueue.RETURN_TIMEappear when the ride offers them.- An entity with nothing to report is absent from the
liveDataarray. A missing entry means no data, not a closed ride. lastUpdatedis when the value last changed.- Live data is cached for 60 seconds. Requesting it more often than once a minute per park returns the same response.
Finding park and ride IDs
Every destination, park, attraction, show and restaurant is an entity with a UUID. Start at the destination list and walk down.
curl https://api.themeparks.wiki/v1/destinations
GET /v1/entity/{id}/children returns what is inside an entity. Asking about a DESTINATION returns every entity in that destination, and asking about a PARK returns every entity in that park. Both return the whole subtree rather than one level. Every other entity type returns its direct children only. Rebuild the hierarchy from each child's parentId.
GET /v1/entity/{id} returns one entity on its own. The id may be a UUID, a URL slug, or, for a destination, its upstream external id.
Park opening hours
GET /v1/entity/{id}/schedule returns opening hours for the next 30 days in the park's own timezone, including extra hours and ticketed events.
curl https://api.themeparks.wiki/v1/entity/75ea578a-adc8-4116-a54d-dccb60765ef9/schedule
For a specific month, including a past one, use GET /v1/entity/{id}/schedule/{year}/{month}. The month is two digits, so /2026/09 is valid and /2026/9 returns 400. Days the park has not published are absent from the response rather than marked closed.
Historical wait time data
The history endpoints return what wait times and ride status were, either as every recorded change through a day or as one summary row per day. Historical data is what the paid plans buy.
GET /v1/entity/{id}/historyreturns one row per change. Each row is the complete state at that moment;changedlists the fields that differ from the previous row.GET /v1/entity/{id}/history/dailyreturns one row per day with operating minutes and wait time minimum, median, mean, p90 and maximum.GET /v1/entity/{id}/history/coveragereturns how far back history goes for that entity, field by field.
Both history endpoints answer a whole park in one request when given a park id, returning an entities array with one entry per entity of the park. Asking park by park rather than ride by ride is the difference between 124 requests and 15,128 for five years of one resort.
Dates are park-local calendar days. A request for 2026-09-15 means that date at the park, in the park's timezone.
opening is the state the requested range starts in. opening.time is the start of the range you asked for and opening.observedAt is when that state was last actually observed. Values are carried forward at any age, so a day with no recorded data still reports a state. Check observedAt before charting it.
| Call | Days per call |
|---|---|
GET /history, one entity | 31 days |
GET /history, a whole park | One day. A park day is every change for a few hundred rides |
GET /history/daily, one entity | 3,660 days (about ten years), never paged |
GET /history/daily, a whole park | 31 days per page; next carries the rest |
| Plan | Reaches back | History requests |
|---|---|---|
| No API key | 7 days | 60 an hour |
| Free | 30 days | 600 an hour |
| Pro | 400 days | 1,200 an hour |
| Business | the full archive | 3,000 an hour |
| Enterprise | the full archive | unmetered |
History requests are metered by a separate hourly budget, independent of the per-minute request limit. Exceeding it returns HTTP 429 with a HISTORY_RATE_LIMITED error body and a retryAfter value in seconds. A 429 is a rate limit, not an empty result: check the response status before reading the body. Requesting history deeper than your plan allows returns HTTP 403 with the earliest date you may request. Per-park coverage is published at themeparks.wiki/coverage.
How to download historical wait time data
Getting the archive into your own store is one loop over a paged endpoint. The official libraries do that loop; the steps below are what it has to get right either way.
Using a library
Install with pip install themeparks for Python or npm install themeparks for JavaScript. Installing either also puts a themeparks-backfill command on your path, and for the common job — get everything we hold into a file — that is the whole thing:
themeparks-backfill --list disney # finding a park needs no key
export THEMEPARKS_API_KEY=tpw_your_key # how far back it reaches is your plan
themeparks-backfill "magic kingdom" --format csv --out ./data
Set the key before the second command. Without one it still runs, on the seven days anonymous access allows, and exits successfully — so a key you meant to set and did not is a short file rather than an error.
It asks the API how far back your plan reaches and starts there, writes one file per park named for the park's id, and puts parkId, parkName, entityId, entityName and entityType on every row. If the hourly history budget runs out it records where it got to and exits 75, so a timer retries and the same command carries on. The CSV is byte-for-byte identical from either library.
To own the loop instead, each library exposes entity(id).history with three calls: span() for the date range available to you, days() for one summary row per park-local day, and changes() for every recorded change.
from themeparks import ThemeParks, APIError
with ThemeParks(api_key=YOUR_KEY) as tp:
history = tp.entity(PARK_ID).history
span = history.span()
def write(first_day):
for ref, row in history.days_with_entities(first_day, span.retrievable_through):
store(ref, row)
try:
write(span.archive_from)
except APIError as exc:
# Your plan may not reach the archive's first day. The 403 names the day
# you may start from, so start there rather than guessing.
body = exc.body.get("error", {})
if body.get("type") != "HISTORY_WINDOW_EXCEEDED":
raise
write(body["earliestAllowedDate"])
The equivalent in JavaScript is for await (const { entityId, row } of history.days({ from, to })). Both page automatically and yield rows as they arrive, so a resort's five years never has to fit in memory.
Three things to get right
- Fetching ride by ride instead of park by park. Give the history endpoints a park id and one request answers every entity in that park. Disneyland Resort's five years of daily history is 124 park-level requests, against 15,128 per-entity ones for the same data.
- Reading the first response and stopping. A park daily request serves at most 31 days and returns
next, an absolute URL for the following page. Follow it until it is null. Stopping at the first response silently gives you one month of a five-year archive. - Starting the range at the archive's first day rather than the first day you may read.
span()reports the archive's floor, which on every plan short of the full archive is earlier than your entitlement. Passing it as a range start returns 403 on the first request, and the body names the day you may start from — read it and start there. - Ending the range at the newest day held rather than the newest day you may read. The coverage document reports both.
retrievableThroughis what your API key is entitled to;recordedTois what exists. On every plan below the top one those are different dates, so a range bounded by the wrong one runs for hours and then returns 403 for the remainder.
Keeping a database current after the backfill
The archive is written two to three days behind live data, so a nightly job that asks only for yesterday will miss days that land late. Ask for the last seven days on every run and upsert on entity and date. It costs one request per park. A row for today is partial by design: operatingMinutes counts only the minutes already elapsed, so it grows through the day and is final once the day ends.
Absent days are not zero
A day with no data has no row at all, and the standby statistics are omitted rather than null when an entity published no numeric wait. That is deliberate: a row of zeroes would be a different claim, meaning observed and closed all day. Filling the gaps with zeroes before training on the data teaches a model that queues vanish on the days nobody was recording.
Running out of the hourly budget
History requests are metered hourly, separately from the per-minute limit, so a full backfill will exhaust the budget and the server may ask you to wait most of an hour. Both libraries refuse to sleep through a wait that long: past two minutes they raise BudgetExhaustedError, carrying the seconds the server asked for, so a backfill can record the last day it wrote and resume rather than appearing to hang. Re-read that last day on resume, because a page can end partway through a day.
Resume from the day the server's next URL starts on, never from the newest row you wrote: an entity that stopped reporting has no rows for the tail days of its page, so the newest row is earlier than the page covered and resuming there re-downloads days you already hold. themeparks-backfill does this for you.
Authentication
Live data, opening hours and the entity tree need no API key. An API key raises your request allowance and reaches further back into history. Generate one from your ThemeParks.wiki profile and send it in the x-api-key request header.
curl -H "x-api-key: YOUR_KEY" https://api.themeparks.wiki/v1/destinations
Keys are shown once when generated. The server stores only a one-way hash.
Rate limits
- On the free tier, 300 requests per minute. With an API key the allowance is counted against your account, so every key on it draws from the same bucket. Without a key it is counted per IP address, which you may be sharing. It is a ceiling rather than a target: polling every destination's live data every five minutes sits well inside it. A free key also reaches 30 days of history. Higher limits come with the paid plans, and every plan's figures are on the pricing page.
- For accounts that already exist, these limits only ever move up. If a published free-tier limit is reduced, the reduction applies only to accounts created on or after the date of that change.
- Exceeding the limit returns HTTP 429 with a
Retry-Afterheader giving the seconds to wait. Wait that long before retrying: the budget is already spent and each retry spends more of it. - Every response carries
RateLimit-Limit,RateLimit-Policy,RateLimit-RemainingandRateLimit-Resetheaders, with noX-prefix.
Endpoints
Every path is relative to https://api.themeparks.wiki.
| Endpoint | Returns |
|---|---|
/v1/destinations | Every destination we cover, with its parks |
/v1/entity/{id} | One entity: a destination, park, attraction, restaurant or show |
/v1/entity/{id}/children | Everything inside an entity: a park's rides, a destination's parks |
/v1/entity/{id}/live | Wait times, status and showtimes as they are right now |
/v1/entity/{id}/schedule | Opening hours for the next 30 days |
/v1/entity/{id}/schedule/{year}/{month} | Opening hours for one specific month |
/v1/entity/{id}/history | How wait times and status changed over a day, one row per change |
/v1/entity/{id}/history/daily | One row per day: operating minutes, and wait time min/median/mean/p90/max |
/v1/entity/{id}/history/coverage | How far back history goes for this entity, field by field |
Client libraries and specification
- OpenAPI specification: every endpoint, parameter and response schema. Available as JSON or YAML, and usable with any OpenAPI client generator.
- JavaScript and TypeScript library, published to npm as
themeparks. - Python library.
- parksapi, the open-source collector that gathers the data. Provided for reference; it runs without park credentials.
Frequently asked questions
Is the ThemeParks.wiki API free?
Yes. Live wait times, ride status, showtimes and opening hours are free to use, including in commercial products, and need no API key. Paid plans add deeper historical data, higher request limits and the websocket feed.
Do I need an API key for theme park wait times?
No. Live wait times require no API key. A free API key raises your request allowance and extends how far back you can read history, currently to 30 days from 7 days without one.
Which theme parks does the API cover?
Hundreds of parks worldwide, including Walt Disney World Resort, Disneyland Resort, Universal, Six Flags, Cedar Fair, Merlin and many independent operators. GET /v1/destinations returns the full current list.
How often is the wait time data updated?
Wait times are collected continuously and responses are cached for 60 seconds. Requesting live data more than once a minute per park returns the same response.
How far back does the historical data go?
It varies by park, and the earliest records date from 2021. GET /v1/entity/{id}/history/coverage reports exactly what is held for one entity, and themeparks.wiki/coverage publishes the same per park.
How do I download the full history for a park?
Install the official Python or JavaScript library (pip install themeparks, or npm install themeparks) and iterate entity(parkId).history.days() between the dates span() reports. Give it a park id rather than a ride id: one request then answers every entity in the park, which is 124 requests for five years of Disneyland Resort instead of 15,128. The library follows the paging links and handles the hourly history budget for you.
Why does my history backfill stop after 31 days?
A park daily request serves at most 31 park-local days per page and returns a next field holding an absolute URL for the following page. Follow next until it is null. Both official libraries do this automatically; a hand-written loop that reads only the first response gets one month of the archive and no error.
Can I republish or resell ThemeParks.wiki data?
No. You may build any product on the data, including a paid one, but you may not re-serve it as a data feed or API of your own, or let your users bulk-export it out of your product. Downloading it for your own use is fine.