# Build with AI agents

> llms.txt, Markdown pages and the OpenAPI spec for coding agents and assistants.

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

These docs are written to be read by coding agents and AI assistants as well
as people. Give your agent one of these and it has everything it needs.

| Resource | URL | Use it for |
| --- | --- | --- |
| llms.txt | [`/llms.txt`](https://api.tailend.dev/llms.txt) | An index of every docs page, with agent instructions at the top |
| llms-full.txt | [`/llms-full.txt`](https://api.tailend.dev/llms-full.txt) | Every page in one Markdown file |
| Page as Markdown | add `.md` to any docs URL, e.g. [`/docs/quickstart.md`](https://api.tailend.dev/docs/quickstart.md) | One page, clean, for a context window |
| OpenAPI spec | [`/docs/openapi.json`](https://api.tailend.dev/docs/openapi.json) | Generating a client or tool definitions; the source of truth |

Every page also has a **Copy page** button that copies its Markdown.

## Rules for an agent

- Base URL `https://api.tailend.dev`; every endpoint is a `GET`.
- Send `X-API-Key` on every request. Keep the key on the server, never in a
  prompt, a browser bundle or a URL.
- Start with `GET /v1/metros` for the metro slug.
- Branch on `error.code`, not the status. Both limits return `429`:
  `rate_limited` can be retried after `Retry-After`, `quota_exceeded` cannot.
  See [Errors](https://api.tailend.dev/docs/errors.md).
- An empty list is a real answer, not an error. On `/around`, check `coverage`
  before saying an area has nothing.

## Which endpoint to give a tool

| The user asks | Tool endpoint |
| --- | --- |
| "Find a swim class for my 6-year-old" | [`GET /v1/{metro}/search`](https://api.tailend.dev/docs/api/search-listings.md) |
| "What's on this weekend?" | [`GET /v1/{metro}/events`](https://api.tailend.dev/docs/api/list-events.md) with `from`/`to` |
| "What's it like for families near this address?" | [`GET /v1/{metro}/around`](https://api.tailend.dev/docs/api/search-around-address.md) |
| "Is this a lively area?" (a number only) | [`GET /v1/{metro}/activity`](https://api.tailend.dev/docs/api/get-activity.md) |

## Example tool definition

A function-calling definition for the address search, ready for an
Anthropic or OpenAI tool list. Your tool handler sends the arguments as query
parameters to `GET /v1/{metro}/around`.

```json
{
  "name": "search_around_address",
  "description": "Search an address and get back what is around it for families: dated events, weekly programs, classes, camps and places, grouped and ranked, each with its distance in metres and walking minutes. Also returns a 0-100 activity score, counts for filtering, and a coverage block saying how many nearby listings cannot be placed on the map.",
  "input_schema": {
    "type": "object",
    "properties": {
      "metro": { "type": "string", "default": "philadelphia" },
      "address": { "type": "string", "maxLength": 300, "description": "Street address to search around. Give this OR lat+lng, not both." },
      "lat": { "type": "number", "minimum": -90, "maximum": 90 },
      "lng": { "type": "number", "minimum": -180, "maximum": 180 },
      "radius_m": { "type": "integer", "minimum": 100, "maximum": 8000, "default": 1600, "description": "1600 is about a mile, walking distance for a family." },
      "from": { "type": "string", "description": "YYYY-MM-DD in the metro's timezone. Defaults to today." },
      "to": { "type": "string", "description": "YYYY-MM-DD, inclusive. Defaults to from + 30 days." },
      "ages": { "type": "array", "items": { "type": "string", "enum": ["baby", "toddler", "young_kids", "bigger_kids"] } },
      "free": { "type": "boolean" },
      "types": { "type": "array", "items": { "type": "string", "enum": ["event", "class", "camp", "place"] } },
      "limit": { "type": "integer", "minimum": 1, "maximum": 50, "default": 10, "description": "Per group, not per response." }
    },
    "required": ["metro"]
  }
}
```

Prefer `lat`/`lng` when your app already has coordinates: address lookups have
their own usage limits, and the coordinate form always works.

> **TODO (owner):** decide whether answers built on the API must carry an
> attribution line (for example "Activity data from Tailend, as of {date}"),
> and add the exact wording here.
