You have a family assistant that can chat, plan and remember. Ask it what is on Saturday, though, and it goes vague, or it names the five places everyone names. It does not know about the pottery class two blocks from your house or the storytime that only meets twice a month.
Here is how to fix that in an afternoon, with three small tools and a handful of ground rules. This is a walk-through, so the examples are trimmed. The API reference has every parameter and field.
1. How do you get set up?
Ask for a key (issuance is not self-service yet) and put it in X-API-Key on every request, from your server, never from a browser. Then ask which metros exist, so you are not hard-coding a city:
curl -H "X-API-Key: $TJ_KEY" https://api.tailend.dev/v1/metrosToday the list has one live metro, Philadelphia, with the slug philadelphia. Every other path starts with a metro slug.
2. Which three tools does the agent need?
- Find things.
GET /v1/{metro}/searchwith a free-textq, plus filters:types,fromandto(local dates),age,free,lat/lng/radius_m, andsort(relevance,soonest,nearestornewest). - What is around here.
GET /v1/{metro}/aroundwithlat/lng(or anaddress),radius_m,from,to,agesandfree. It returns results in groups and says how much it cannot see. - Tell me more.
GET /v1/events/{id}, or the matching/v1/classes/{id},/v1/camps/{id}and/v1/places/{id}.
Here is the first tool in function-calling form:
{
"name": "find_things_to_do",
"description": "Search local events, classes, camps and places. Order by 'soonest' or 'nearest'; pass the child's age.",
"input_schema": {
"type": "object",
"properties": {
"q": { "type": "string" },
"from": { "type": "string", "description": "YYYY-MM-DD, local" },
"to": { "type": "string", "description": "YYYY-MM-DD, local" },
"age": { "type": "integer" },
"sort": { "type": "string", "enum": ["relevance", "soonest", "nearest", "newest"] },
"lat": { "type": "number" },
"lng": { "type": "number" },
"radius_m": { "type": "integer" }
},
"required": ["q"]
}
}The handler is a thin pass-through:
async function tailend(path, params) {
const url = new URL(path, "https://api.tailend.dev");
for (const [k, v] of Object.entries(params)) if (v != null) url.searchParams.set(k, v);
const res = await fetch(url, { headers: { "X-API-Key": process.env.TJ_KEY } });
return res.ok ? res.json() : { error: (await res.json()).error };
}
// find_things_to_do -> tailend("/v1/philadelphia/search", args)Every failure comes back in one envelope, { "error": { "code", "message", ... } }, so the agent can read it. On a 429, wait for the Retry-After header.
3. What ground rules make it good at the long tail?
This part matters more than the code. Put these in the agent's instructions:
- Order by when and how near, not how famous. Use
sort=soonestorsort=nearest. - Always pass the child's age. It turns a long list into a short, right one.
- Read
coverage.hidden_nearbyon/aroundresults. If it is above zero, say some nearby things could not be placed on a map. An empty group does not mean a quiet neighborhood. - If
has_dateisfalse, say the date is not stated. Never make one up. - Say when it was last confirmed. Report
verified_atas "as of," not as a promise.
4. What does one useful call look like?
"What is on this weekend, within a mile of us, for a four-year-old?"
- lat39.9489
- lng-75.2098
- radius_m1600
- from2026-10-03
- to2026-10-04
- agesyoung_kids
curl -G -H "X-API-Key: $TJ_KEY" https://api.tailend.dev/v1/philadelphia/around \
--data "lat=39.9489" --data "lng=-75.2098" --data "radius_m=1600" \
--data "from=2026-10-03" --data "to=2026-10-04" --data "ages=young_kids"The response groups what it found (happening_soon, recurring_programs, classes_enrolling, camps, places). Each item carries name, distance_m, walk_minutes, next_local, is_free and a registration_url. walk_minutes is a straight-line estimate, so let the agent say "about a five-minute walk," not "exactly."
A weekly class sits in both happening_soon and recurring_programs on purpose. Each item lists the other groups it is also_in, so the agent can mention it once.
5. How do you keep the answers honest?
Before the agent tells a parent to leave the house, have it fetch the detail (GET /v1/events/{id}) for the one thing it is about to recommend. That is one extra call for the one listing that matters, and it is the cheapest way to catch a change. A follow-up post covers keeping recommendations fresh as the date approaches.
Why this matters
The first time your agent finds something a parent did not know about, it stops being a search box. It starts being useful. The rules above cost you a few sentences of instructions, and they are the difference between an agent that repeats the internet and one that knows a neighborhood.
How Tailend helps
Tailend reads events, classes, camps and places from the organizers' own pages and calendars into consistent, dated records, checks them more closely as their dates approach, and tells you when it cannot see something. We built it because we ran into every one of these problems running TinyJawns, a family guide in Philadelphia. We start there and are working outward from there.
