# Quickstart

> Make your first request, narrow it by date, age and location, and read the next page.

Source: https://api.tailend.dev/docs/quickstart

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](https://api.tailend.dev/docs/authentication.md)). 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.

```bash
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.

```bash
curl -H "X-API-Key: $TAILEND_API_KEY" https://api.tailend.dev/v1/metros
```

```javascript
const 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"
```

```python
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"
```

```json
{
  "data": [
    { "slug": "philadelphia", "name": "Philadelphia Area", "timezone": "America/New_York", "aliases": ["philly", "phl"], "is_active": true }
  ]
}
```

Now list events in that metro:

```bash
curl -H "X-API-Key: $TAILEND_API_KEY" https://api.tailend.dev/v1/philadelphia/events
```

```json
{
  "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 |

```bash
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"
```

```javascript
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_confidence` is `0.7` for events, classes and camps, and `0.5` for
  places. See [How we verify listings](https://api.tailend.dev/docs/how-we-verify.md).
- Adult-only listings are left out. See
  [Audience & versioning](https://api.tailend.dev/docs/audience-and-versioning.md).

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](https://api.tailend.dev/docs/api.md) 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.

```bash
curl -sS -H "X-API-Key: $TAILEND_API_KEY" --get https://api.tailend.dev/v1/philadelphia/events \
  --data-urlencode "cursor=$NEXT_CURSOR"
```

```javascript
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](https://api.tailend.dev/docs/pagination.md).

## 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](https://api.tailend.dev/docs/search.md).

## Next steps

- [API reference](https://api.tailend.dev/docs/api.md): every endpoint, field and status code
- [Search](https://api.tailend.dev/docs/search.md): one query across every collection
- [Shaping responses](https://api.tailend.dev/docs/shaping-responses.md): `fields=`, `expand=` and `include=`
- [Errors](https://api.tailend.dev/docs/errors.md): the envelope every failure uses
- [Limits & usage](https://api.tailend.dev/docs/limits-and-usage.md): rate limit and monthly quota
