Get started
Quickstart
Make your first request, narrow it by date, age and location, and read the next page.
Get a first real result in about five minutes: a key, a first request, a filtered request and the next page. Everything is plain HTTPS with an API key; no SDK is required.
1. Get a key
Key issuance is not self-service yet: ask us and we set one up for your account (see Authentication). Keep it in an environment variable and send it in the X-API-Key header on every request. Never put it in a URL or in browser code.
export TAILEND_API_KEY="your key"2. Make your first request
Most paths start with a metro slug, so ask for the list first instead of hard-coding one.
curl -H "X-API-Key: $TAILEND_API_KEY" https://api.tailend.dev/v1/metrosconst res = await fetch("https://api.tailend.dev/v1/metros", {
headers: { "X-API-Key": process.env.TAILEND_API_KEY },
});
const { data: metros } = await res.json();
console.log(metros[0].slug); // "philadelphia"import os, requests
res = requests.get(
"https://api.tailend.dev/v1/metros",
headers={"X-API-Key": os.environ["TAILEND_API_KEY"]},
)
print(res.json()["data"][0]["slug"]) # "philadelphia"{
"data": [
{ "slug": "philadelphia", "name": "Philadelphia Area", "timezone": "America/New_York", "aliases": ["philly", "phl"], "is_active": true }
]
}Now list events in that metro:
curl -H "X-API-Key: $TAILEND_API_KEY" https://api.tailend.dev/v1/philadelphia/events{
"data": [
{
"id": "b81a95d2-1d4e-46fd-9cff-322a41e429ad",
"type": "event",
"name": "Toddler Time (Ages 5 and Under)",
"starts_local": "2026-09-14T09:30:00",
"starts_at": "2026-09-14T13:30:00.000Z",
"has_date": true,
"timezone": "America/New_York",
"place": { "id": "9cde4494-6c2a-4bfc-b15a-22edce13f3f7", "slug": "treehouse-world-d34361f7", "name": "Treehouse World" },
"price": { "is_free": false, "min": 12, "max": 12, "currency": "USD" },
"confidence": 1,
"links": { "self": "/v1/events/b81a95d2-1d4e-46fd-9cff-322a41e429ad" }
}
],
"meta": { "limit": 50, "count": 1, "has_more": false, "next_cursor": null, "min_confidence": 0.7 }
}3. Narrow it down
Filters compose, so send as many as you need in one request.
| Filter | Example | Keeps |
|---|---|---|
| Dates | from=2026-09-19&to=2026-09-21 | Listings on those local dates, inclusive, in the metro's timezone |
| Age | age=4 | Listings whose stated age range includes 4 |
| Location | lat=39.9489&lng=-75.2098&radius_m=1600 | Listings within 1.6 km (100 to 80,000 metres) |
| Price | free=true | Free listings only |
| Text | q=story time | Listings matching the words |
curl -sS -H "X-API-Key: $TAILEND_API_KEY" --get https://api.tailend.dev/v1/philadelphia/events \
--data-urlencode "from=2026-09-19" \
--data-urlencode "to=2026-09-21" \
--data-urlencode "age=4" \
--data-urlencode "lat=39.9489" \
--data-urlencode "lng=-75.2098" \
--data-urlencode "radius_m=1600"const params = new URLSearchParams({
from: "2026-09-19",
to: "2026-09-21",
age: "4",
lat: "39.9489",
lng: "-75.2098",
radius_m: "1600",
});
const res = await fetch(`https://api.tailend.dev/v1/philadelphia/events?${params}`, {
headers: { "X-API-Key": process.env.TAILEND_API_KEY },
});
const { data } = await res.json();Defaults you get without asking
A few filters apply even when you do not send them:
- Events cover the next 30 days.
min_confidenceis0.7for events, classes and camps, and0.5for places. See How we verify listings.- Adult-only listings are left out. See Audience & versioning.
Every list response reports the filters it used in meta.applied_filters, and names the ones that came from a default in meta.defaults_applied. The API reference lists every filter for each endpoint.
4. Get the next page
Every list response carries meta.next_cursor. Pass it back unchanged to get the next page; it is null on the last one.
curl -sS -H "X-API-Key: $TAILEND_API_KEY" --get https://api.tailend.dev/v1/philadelphia/events \
--data-urlencode "cursor=$NEXT_CURSOR"let cursor = null;
do {
const params = new URLSearchParams(cursor ? { cursor } : {});
const res = await fetch(`https://api.tailend.dev/v1/philadelphia/events?${params}`, {
headers: { "X-API-Key": process.env.TAILEND_API_KEY },
});
const { data, meta } = await res.json();
for (const event of data) console.log(event.name);
cursor = meta.next_cursor;
} while (cursor);Treat the cursor as opaque, and keep the same filters while you page. See Pagination.
Search across collections
Not sure whether the answer is an event, a class, a camp or a place? Search all of them at once with GET /v1/{metro}/search?q=…. See Search.
Next steps
- API reference: every endpoint, field and status code
- Search: one query across every collection
- Shaping responses:
fields=,expand=andinclude= - Errors: the envelope every failure uses
- Limits & usage: rate limit and monthly quota