Trust & stability
How we verify listings
Where a listing comes from, and what confidence and verified_at tell you.
This page is for anyone deciding how much to trust a listing before showing it to a user — what "verified" means here, where the numbers come from, and which fields to read if you want to show your own trust signal instead of just taking ours.
curl -sS -H "X-API-Key: $TAILEND_API_KEY" --get https://api.tailend.dev/v1/philadelphia/events \
--data-urlencode "min_confidence=0.8" \
--data-urlencode "include=source" \
--data-urlencode "limit=1"const res = await fetch(
"https://api.tailend.dev/v1/philadelphia/events?min_confidence=0.8&include=source&limit=1",
{ headers: { "X-API-Key": process.env.TAILEND_API_KEY } },
);
const { data } = await res.json();
console.log(data[0].confidence, data[0].verified_at, data[0].source.verification.status);Where a listing comes from
Every event, class, camp and place starts from an organizer's own page — their site, a calendar system, a listing directory. We don't invent dates, prices or descriptions: what you get back is what the source states, read into a consistent shape. When a value isn't on the source page, the field is null, not a guess.
Re-checking
Listings are re-checked against their source over time, prioritized around what's about to happen — an event coming up soon, a registration deadline, an enrollment window — rather than checked on a flat schedule regardless of date. There's no published SLA with a numeric guarantee yet, so this page won't quote a specific "checked every N hours" promise. What you can rely on today is the two fields below, which tell you — per listing, right now — whether and when it was last checked.
The fields
Every response carries two flat fields, on every resource, always:
| Field | Meaning |
|---|---|
confidence | 0–1 trust score. The same number min_confidence filters on |
verified_at | UTC instant of the last check against the source, or null if the listing hasn't been checked yet |
confidence starts at a documented baseline for a newly parsed listing and becomes a verified score once a check has run; verified_at is null until then. Neither field tells you why — for that, ask for include=source (a paid add-on — see Plans & access), which attaches:
| Field | Meaning |
|---|---|
source.url | The page the listing was parsed from |
source.crawled_at | When that page was last fetched |
source.verification.status | The outcome of the last check: clear (still matches), changed (source has changed since), flagged (a specific problem was found, evidenced), unverifiable (couldn't be confirmed either way), unreachable (source didn't respond), gone (source no longer has it), or null before any check |
source.verification.basis | verified once a check has produced the score, else baseline |
source.verification.date_basis | How the date was established: stated (the source gave it directly), inferred (derived, e.g. from a recurrence rule), unevidenced, or unknown |
See the API reference for the full shape.
Fields providers often leave unstated
null on a value field means not stated by the source. It is never a "no": a class with adult_participation: null may or may not need a parent to stay, and drop_in_price: null does not mean drop-ins are unavailable. We only fill these from what the provider's own text states; we never work them out from dates, ages or category, and we leave a field empty rather than fill it with a value we are not sure is right.
Some class fields are stated by far fewer providers than others. Plan your UI for null on these rather than treating it as missing data:
| Field | What null means | Notes |
|---|---|---|
program.adult_participation | Not stated for this class | Values: required, optional, drop_off, not_allowed, varies. Many providers put this rule on an FAQ or policy page rather than on the class |
tuition.drop_in_price | No single-class price stated for everyone | Offered by a minority of providers; member-only or age-specific prices are not filled |
tuition.trial_price | No trial price stated | 0 is a free trial; conditional trials are not filled |
term.total_classes | No series length stated by the class's own text | Rolling, drop-in, open and year-round classes have none |
registration.spots_remaining | The provider does not publish a seat count | Rarely published; read registration.status instead. registration.has_spots_available is true unless we have read a full/closed statement, so it is not a guarantee of open seats |
Schedule and price fields (schedule.days, schedule.start_time, schedule.duration_minutes, tuition.amount) are stated more often but still not by every provider, and follow the same rule: null is "not stated", not "none".
min_confidence
Every list endpoint drops rows below a confidence floor by default — you never have to ask for this, and you can always loosen or tighten it:
| Resource | Default min_confidence |
|---|---|
| Places | 0.5 |
| Events, classes, camps | 0.7 |
Pass min_confidence=0 to see everything regardless of score, or a higher value to see only what's cleared a stricter bar. place_id-scoped and organization_id-scoped lists use the same default as their resource.
What "fresh" means, honestly
Two different things get called "freshness," and it's worth keeping them apart when you're deciding what to show a user:
- When we last looked —
verified_at/source.crawled_at. This is a fact about our process, always available per listing. - Whether what we found is still true —
source.verification.status. Aclearresult from three days ago is a stronger signal than achangedorunverifiableresult from this morning.
Neither one is a promise that seats are still open or a price hasn't changed since the last check — for time-sensitive facts like registration status, read the listing's own registration/enrollment fields and treat verified_at as "as of," not "guaranteed as of this second."
What to do
- Use
confidence/min_confidenceas your baseline trust filter; only reach forinclude=sourcewhen you want to show your own "last checked" or "verified" badge to a user. - Branch on
source.verification.status, not onverified_atalone — a listing can be checked recently and still come backunverifiableorflagged. - Don't build on a specific re-check interval as a guarantee; build on the per-listing fields above, which are accurate regardless of how the underlying schedule evolves.