Skip to content
GET/v1/{metro}/searchall plans

Search every listing collection

Searches published, active listings in one metro. When semantic search is enabled, results match by meaning as well as keywords. Search applies the same visibility and filter rules as each list endpoint, so it never returns a listing that endpoint would hide. Results are ranked and paginated across collections. Recurring events use their next occurrence.

Show more

How words are matched. The query is read as plain words: quotes, a leading - and OR are not interpreted. Different forms of a word match each other (swim finds swimming), and a short list of closely related family-activity words also counts (pottery finds ceramics, baking finds cooking). Question and filler words (where can I, with, for, the) are ignored and never change a score. Words that only name the kind of listing (class, lessons, camp) never decide whether a listing matches; they only lift a listing that has them in its name.

Ages and audiences. An age in the query becomes an age filter: 5 year olds, ages 3-5, toddlers (1 to 3), baby (0 to 1) and teens (13 to 18). The band that was applied is echoed in meta.interpreted.age. When the query gives an age, listings whose stated ages cover it rank above listings with no stated ages; listings whose ages exclude it are left out. An explicit age or audience parameter always wins: the age in the words is then not applied, and meta.interpreted.age.overridden_by says which parameter won. Time and price words (tonight, saturday, morning, cheap) are not filters yet: they never decide a match and are listed in meta.ignored_words. Audience words (parents, kids, family) do not narrow the search when other words are present, and are listed in meta.ignored_words; a title that names them still ranks a little higher. A query made only of such words is never refused: Mommy and Me, kids and family search for those words as written. A query with nothing searchable at all (near me, where can I) returns 400.

How directly a result matches. Every result carries match. all_terms: every word you typed is in the listing's name or category, or is prominent in the opening of its description. related: every word is covered that way, but at least one only through a closely related word. partial: only some of your words, or all of them only mentioned in passing further down a description (food in a park concert that sells snacks). Inside a level, a name match ranks above a category match, which ranks above a description match. By default only all_terms and related are returned, so a count reflects real matches; send match=all_terms,related,partial (or match=partial alone) to include the loose ones. meta.match_counts says how many results there are at each level, whatever match you chose, so a client can say "17 direct, 36 related, 1,378 loose". Results are ordered so that all_terms come before related, and those before partial, unless you choose another sort.

score is an absolute 0 to 1 measure of how well one listing fits the words of the query. It is not scaled against the other results, so a weak best match no longer shows as 1, and the same listing gets the same score whatever else is returned. It is not a probability. snippet is a plain-text, unhighlighted excerpt of the listing's public description (or name when no description exists), at most 180 characters.

sort=relevance is the default. soonest orders dated results by their next occurrence (including recurring events), with places and undated items after them. nearest orders by distance and requires lat + lng or near_zip. newest orders by created_at. Every order breaks ties by score, then id; cursors are bound to the sort and ranking mode. If semantic availability changes between pages, the API returns 400 bad_request with details.reason: cursor_expired; restart the search without a cursor so results are never silently skipped.

Try this requestRun it with sample data, no key needed. Paste a key to run it live.
Example request
curl -sS \
  -H "X-API-Key: $TAILEND_API_KEY" \
  "https://api.tailend.dev/v1/philadelphia/search?q=family%20story%20time"
Example response · 200
{
  "data": [
    {
      "type": "event",
      "id": "b81a95d2-1d4e-46fd-9cff-322a41e429ad",
      "slug": "toddler-storytime",
      "name": "Toddler Storytime",
      "snippet": "Stories and songs for children ages 2–5.",
      "match": "all_terms",
      "score": 0.92,
      "starts_local": "2026-10-03T10:00:00"
    }
  ],
  "meta": {
    "query": "toddler story time",
    "applied_filters": {
      "audience": [
        "not_adults"
      ],
      "by_type": {
        "events": {
          "from": "2026-09-28",
          "to": "2026-10-28",
          "min_confidence": 0.7
        },
        "classes": {
          "from": "2026-09-28",
          "to": null,
          "min_confidence": 0.7
        },
        "camps": {
          "from": "2026-09-28",
          "to": null,
          "min_confidence": 0.7
        },
        "places": {
          "min_confidence": 0.5
        }
      }
    },
    "defaults_applied": [
      "from",
      "to",
      "min_confidence",
      "audience"
    ],
    "types": [
      "events",
      "classes",
      "camps",
      "places"
    ],
    "ranking": "keyword",
    "ignored_words": [],
    "interpreted": {
      "age": {
        "min": 1,
        "max": 3,
        "from": "toddlers"
      }
    },
    "match": [
      "all_terms",
      "related"
    ],
    "match_counts": {
      "all_terms": 1,
      "related": 0,
      "partial": 14
    },
    "sort": "relevance",
    "limit": 20,
    "count": 1,
    "count_estimate": 1,
    "capped": false,
    "has_more": false,
    "next_cursor": null,
    "generated_at": "2026-09-28T14:30:00Z"
  }
}

Try any request with sample data, no key needed. Available on every plan.

Metro slug from /v1/metros (e.g. philadelphia).

Natural-language or keyword query, read as plain words.

Optional parameters (42)

Repeatable or comma-separated collections; defaults to all four.

Cross-collection ordering.

Which match levels to return, as a comma-separated list of all_terms, related and partial.

Local calendar date (metro timezone), inclusive.

Local calendar date (metro timezone), inclusive; at most 366 days after from.

Drop rows whose confidence is below this.

Keep rows whose categories contain any of these.

Keep rows whose audience is one of these.

A child's age in years.

Neighborhood slug or free-text name (case-insensitive).

Center latitude for a radius search.

Center longitude for a radius search.

Radius in metres around lat/lng (or near_zip, where enabled).

Beta — may not be enabled for all keys yet.

Beta — may not be enabled for all keys yet.

Beta — may not be enabled for all keys yet.

Events, classes, and camps only; ignored for places.

A budget.

true keeps only events with a stated clock time.

Filter on is_recurring.

Keep rows on any of these local days.

Events, classes and camps only.

Events and classes tagged for a seasonal occasion.

Events built around a local pro sports team (watch parties, game-day themed events, team or mascot appearances).

Local start time at or after this, HH:MM (or HH:MM:SS).

Local start time strictly before this, HH:MM; 09:00–12:00 is the morning and 12:00 belongs to the afternoon.

Keep rows published by this organization (organization.id).

Sync mode.

Keep rows held at this place (place.id).

Classes only.

Classes only; keep classes meeting on this weekday.

Classes only; filter by enrollment model.

Camps only; program year.

Camps only; filter overnight programs.

Places only; filter by place type.

Places only; filter advertised birthday-party offerings.

Places only; filter places offering classes.

Events and classes only; see the indoor parameter on the event list.

Events and classes only; see the outdoor parameter on the event list.

Events, classes and places; see the sensory parameter on the event list.

Opaque continuation token, bound to the query, types, filters, and sort.

Results per page.

Ctrl Enter to send