Find listings
Market overview with facets
Counts by category, weekday, time, price, age and organization over a whole filtered set.
This page is for anyone showing "the market near here": how many classes and events, on which days, at what times and prices, for which ages, run by whom. You can get those numbers without downloading the listings. Add facets= to a list request and the response carries counts over every listing that matches your filters, not just the page you asked for.
It also covers the other half of the same question: when a count looks smaller than you expected, the response tells you which filters produced it.
Two things to know first
Three filters narrow a list even when you do not send them:
| Filter | Default | Applies to |
|---|---|---|
from / to | from is today in the metro; on events to is from + 30 days | events; classes and camps default only from, so their window is open-ended |
min_confidence | 0.7 (0.5 on places) | events, classes, camps, places |
audience | everything except adults | events, classes, camps, places |
A request with none of them sent therefore sees one month of events and leaves out lower-confidence and adult-audience listings. That is the right default for a family product, but it is easy to forget when you compare a list against a market-wide total.
Every list response says what it used. meta.applied_filters holds the value in effect for each filter the endpoint applies, and meta.defaults_applied names the ones that came from a default rather than from you:
curl -sS -H "X-API-Key: $TAILEND_API_KEY" --get https://api.tailend.dev/v1/philadelphia/events \
--data-urlencode "lat=39.9526" \
--data-urlencode "lng=-75.1652" \
--data-urlencode "radius_m=8000" \
--data-urlencode "limit=1"{
"meta": {
"applied_filters": {
"from": "2026-09-30",
"to": "2026-10-30",
"min_confidence": 0.7,
"audience": ["not_adults"],
"lat": 39.9526,
"lng": -75.1652,
"radius_m": 8000
},
"defaults_applied": ["from", "to", "min_confidence", "audience"]
}
}To widen the view, send the filter yourself: to=2027-09-29 for a year of events, min_confidence=0 for everything regardless of confidence, audience=all to include adult listings. The echo then shows your value and drops the name from defaults_applied. The echoed value is always one you can send back: audience=not_adults (the default) returns exactly the set you got without the parameter.
The same two fields appear on events, classes, camps, places, /search, /nearby and /around. Only filters that endpoint applies are listed, so a weekday sent to /camps does not appear: the endpoint ignored it.
Ask for the counts
facets takes any of category, weekday, time_block, price_band, age_band and organization, comma-separated. Add limit=0 when you only want the counts:
curl -sS -H "X-API-Key: $TAILEND_API_KEY" --get https://api.tailend.dev/v1/philadelphia/classes \
--data-urlencode "lat=39.9526" \
--data-urlencode "lng=-75.1652" \
--data-urlencode "radius_m=8000" \
--data-urlencode "facets=category,weekday,time_block,price_band,age_band,organization" \
--data-urlencode "limit=0"const params = new URLSearchParams({
lat: "39.9526",
lng: "-75.1652",
radius_m: "8000",
facets: "category,weekday,time_block,price_band,age_band,organization",
limit: "0",
});
const res = await fetch(`https://api.tailend.dev/v1/philadelphia/classes?${params}`, {
headers: { "X-API-Key": process.env.TAILEND_API_KEY },
});
const { facets, meta } = await res.json();
console.log(facets.total, "classes;", meta.defaults_applied);{
"data": [],
"facets": {
"total": 112,
"is_complete": true,
"category": [
{ "value": "arts_creative", "count": 38 },
{ "value": "sports_active_play", "count": 27 },
{ "value": "music_movement", "count": 19 },
{ "value": "unspecified", "count": 4 }
],
"weekday": [
{ "value": "monday", "count": 31 },
{ "value": "tuesday", "count": 36 },
{ "value": "wednesday", "count": 29 },
{ "value": "thursday", "count": 33 },
{ "value": "friday", "count": 18 },
{ "value": "saturday", "count": 44 },
{ "value": "sunday", "count": 12 }
],
"time_block": [
{ "value": "morning", "count": 41 },
{ "value": "afternoon", "count": 39 },
{ "value": "evening", "count": 28 },
{ "value": "unspecified", "count": 4 }
],
"price_band": [
{ "value": "free", "count": 9 },
{ "value": "under_20", "count": 14 },
{ "value": "20_to_50", "count": 35 },
{ "value": "50_to_100", "count": 22 },
{ "value": "100_plus", "count": 17 },
{ "value": "unspecified", "count": 15 }
],
"age_band": [
{ "value": "baby", "count": 14 },
{ "value": "toddler", "count": 33 },
{ "value": "young_kids", "count": 51 },
{ "value": "bigger_kids", "count": 39 },
{ "value": "unspecified", "count": 12 }
],
"organization": {
"distinct": 41,
"top": [
{ "id": "c45b9b3a-45ab-4f9c-b2e2-986dcb921f25", "name": "Example Arts Center", "count": 9 }
],
"other_count": 97,
"unspecified_count": 6
}
},
"meta": { "limit": 0, "count": 0, "has_more": false, "next_cursor": null }
}(Numbers are illustrative.) Without limit=0 you get a normal page in data and the same facets beside it, both from one request.
How to read the counts
totalis the number of listings that match your filters. It is the number you would reach by paging the list to the end.- A listing is counted in every bucket it belongs to. A class that meets Monday and Wednesday is in both; a listing with two categories is in both. So
category,age_bandand a class'sweekdaycan add up to more thantotal.time_blockandprice_bandput each listing in exactly one bucket. unspecifiedcounts listings that do not state that detail (no start time, no price, no age range). It appears only when it is above zero.- Buckets.
weekdayis the local day of an event, or each day a class meets.time_blockismorning(before 12:00),afternoon(12:00 to before 17:00) orevening(17:00 on), from the local start time.price_bandisfreefor exactly the listingsfree=truereturns, so the bucket and the filter give the same number; every other listing is banded by its lowest stated price in US dollars:under_20,20_to_50,50_to_100,100_plus. A listing that is not free and states no price above zero isunspecified.age_banduses the same bands asages.bandson a listing. organizationlists the twenty organizations with the most listings.other_countcovers the rest, andunspecified_countthe listings with no organization.- Not every facet exists on every collection.
weekdayis for events and classes;time_blockandprice_bandare for events, classes and camps. Ask for one a collection has no data for and it is named infacets.unavailableinstead of returning an empty list.
Facets answer the filters you send, so a market overview near a point is the same request you would page, with facets= and limit=0 added. Use the counts to draw the overview, then fetch a page only for the bucket the person opens.
Limits
facets=is available on every plan and counts as one request, however many listings it covers. A facets-only request returns no records.pageandcursordo not change the counts.- The organizations list is capped at twenty.
- Very large sets are counted up to a fixed bound. When a count covers only part of the set, or counting ran out of time,
facets.is_completeisfalse. Narrow the filters (a metro-wide overview is comfortably inside the bound) and ask again. limit=0is accepted only together withfacets; on its own it is400./searchdoes not takefacets.
Related
- Quickstart — filters, pagination and search.
- Pagination — walking a list when you do want the records.
- Audience & versioning — why
adultsis left out by default. - Plans & access — what each plan includes.