API reference
Errors
{
"error": { "code": "burst_needs_date", "message": "A burst near release_date needs something that gives that date: a release_date condition or attribute, or a site that reports it.", "field": "schedule.burst.near" },
"errors": [{ "code": "burst_needs_date", "message": "...", "field": "schedule.burst.near" }]
}error is the first problem. On a 422, errors lists all of them, so you can fix a request in one pass. field is the path in your body. A 422 can also carry a suggestion, a corrected request for you to check and send. See Suggestions.
Status codes
| Status | code | |
|---|---|---|
| 400 | bad_json | The body isn't JSON |
| 401 | unauthorized | Missing, unknown or revoked key |
| 403 | limit_reached | 1,000 active or paused watchers |
| 404 | not_found | No such watcher, or not yours |
| 409 | watcher_finished | Resuming a matched or expired watcher |
| 409 | idempotency_conflict | Same Idempotency-Key, different body |
| 422 | see below | A rule failed |
| 429 | rate_limited | Over 120 requests a minute. See Retry-After |
| 500 | internal | Our fault |
Validation codes
Shape
code | |
|---|---|
invalid_request | A field is missing, misspelled or the wrong type |
value_missing | The op needs a value |
value_not_list | in and not_in need a list |
value_not_number | price, price_drop and quantity need a number, "$20" or "10%" |
value_not_date | Dates look like 2026-12-15 |
value_unknown | Not a status we know, for availability or release_status |
Target and sites
code | |
|---|---|
match_query_shape | url needs a full link on a listed site; sku needs an identifier |
exact_needs_version | exact needs a version in the name; use line to follow every version |
version_needs_line | version conditions need match: line or category |
site_unknown | We can't read that site |
site_unavailable | We can read that site, but not on this server right now |
site_cant_match | That site doesn't support the match mode |
site_not_reviewed | We don't support that site yet. Sites |
site_terms_not_accepted | Set fetch.accept_site_terms for this site |
recipe_not_learned | At create: we couldn't learn how to read that page. message says why; field is the where.sites entry |
recipe_input_missing | The page needs a value the watcher didn't give. message names it, such as location.zip |
llm_budget_reached | Your account's monthly LLM budget is spent, so no new page can be learned until next month |
recipe_dead | We gave up on that page after 3 relearns found nothing. Preview it again to retry |
bad_steps | A browser step, locale, timezone, cookie or location can't be used. message says which |
steps_submit_refused | A step would send a contact, sign-up, registration, subscription, checkout or application form, or post a form after filling more than one field, on a site we haven't reviewed. Search and lookup steps are fine. message says which step |
field_unsupported | None of the sites gives that field |
target_fixed | target can't be updated |
Conditions
code | |
|---|---|
compare_to_needs_numeric | compare_to works on numbers (price, quantity, a numeric attribute), or any field with changed; price_drop, price_change and price_move need it |
percent_needs_compare | A percentage needs compare_to |
window_needs_duration | compare_to: window needs window, like "10m" |
window_needs_compare | window only works with compare_to: window |
min_n_needs_n | require: min_n needs n, no more than the values listed |
over_needs_values | over needs values: a size list, a date range or a listing |
over_too_many | At most 20 values under over |
same_seller_needs_sites | same: seller needs two sites, or a site with several sellers |
list_change_needs_items | added and removed read the whole list: use field items |
items_needs_list_op | Field items takes op added or removed |
list_change_alone | added and removed compare with the last run by themselves; leave out compare_to, window and over |
judge_needs_rule | judge needs the rule as a sentence in value, up to 300 characters |
judge_alone | judge compares with the last run by itself; leave out compare_to, window and over |
item_needs_judge | Field item (the whole result) only works with op judge |
judge_too_many | At most 3 judged conditions on one watcher |
judge_unavailable | This server has no LLM set up, so it can't check judged conditions |
Location and schedule
code | |
|---|---|
zip_needs_us | ZIPs are US only |
radius_needs_zip | A radius needs a zip |
radius_unreadable | Use "25mi" or "40km" |
currency_unsupported | USD only |
start_at_unreadable | start_at isn't a date |
start_after_until | start_at must be before until |
until_unreadable | A date, "+30d", "after_first_match" or "never" |
until_in_past | until already passed |
until_too_far | At most a year |
burst_needs_date | A burst near a date needs something that gives that date |
burst_needs_time | A burst needs at or near, not both |
burst_at_unreadable | burst.at needs a datetime |
burst_faster_than_every | burst.every must be shorter than every |
Notify and follow-ups
code | |
|---|---|
webhook_channel_without_config | channels has webhook but there's no notify.webhook |
webhook_config_without_channel | notify.webhook is set but channels doesn't have webhook |
webhook_needs_https | Webhook URLs use https |
channel_needs_session | API keys use webhook |
then_nested | A then can't hold another then |
then_needs_single_match | then needs until: after_first_match or aggregate: first |
Check issues
A check that ran still lists issues on its snapshot when something about the read is worth knowing.
A check is blocked on a 429, on a 403 or 429 with x-ratelimit-remaining: 0 (GitHub), and on a 400 or 403 whose body says it's a rate limit (Stack Exchange's throttle_violation). retryAfterSeconds comes from Retry-After, then the rate limit's reset time, then an "in N seconds" in the site's message. On readers that ask for it (Jane App), a redirect that leaves the clinic's own address is not_found. Redirects are followed one hop at a time, at most 5, and robots.txt is checked on each host.
The common issues:
| Issue | |
|---|---|
robots_disallowed | robots.txt disallows the page, or the site needs a terms review |
recipe_missing | No recipe has been learned for this page. Preview the watcher, or create it again |
recipe_learned | A recipe was learned on this check |
recipe_learn_failed | We couldn't learn how to read the page |
recipe_nothing_here | The page has nothing like the target (a home page, an error page) |
recipe_broken | The recipe stopped finding its items. After 3 in a row it's learned again |
selector_not_found | A page-change selector or list matched nothing. On the first read the check fails and the detail names up to three selectors that do match; after the selector has matched once, a miss is reported with the check still ok |
region_empty | The part of the page we read had no text |
platform_unknown | A store that isn't Shopify, WooCommerce, Squarespace, BigCommerce or Magento, on a line or category watch |
not_shopify, not_woocommerce, not_squarespace, not_bigcommerce, not_magento | The store isn't on that platform |
no_product_cards | A store page from the right platform, but with no products we could read |
listing_partly_read, variants_partly_read | Some items or options couldn't be read |
list_truncated, range_truncated, range_capped | More results or days than one check reads; the rest are left out |
time_zone_unknown | The site gave times without a zone we could work out |
ticket_classes_unavailable | Eventbrite's ticket classes couldn't be read; the event itself was |
csrf_token_missing | OpenTable didn't hand out the token its availability call needs |
unexpected_answer | The site's API answered in a shape we don't know, so nothing was read |
empty_as_400, empty_as_404 | NHTSA or openFDA said "no results" with an error status |
api_key_refused | The site refused the server's key for it, or answered 400 saying the key is invalid or missing (NCBI, Stack Exchange). The detail has the site's own message |
private_address | The address resolves to a private or local network (localhost, 10.x, 192.168.x, 169.254.169.254, *.railway.internal), which the watcher doesn't fetch. Use a public address |
usage_limit | Your account used its daily browser runs with steps (200) or captcha solves (20). The detail gives the reset time, 00:00 UTC. The check is failed, not blocked, so it doesn't count toward a blocked event |
captcha_not_allowed | The page showed a captcha and this server only solves them on reviewed sites |
site_not_reviewed | The site waits on a terms review, so nothing was fetched |
partial_data, partial_results | The site's answer covers only part of what it lists (a menu missing its group classes, OSV's first page) |
query_refused, search_refused | The open-data portal or USAspending refused the filter in attributes (a bad where, an unknown agency name) |
radius_checked_after_fetch | The dataset keeps coordinates as text, so only the newest rows read were checked against the radius |
ticket_types_not_read | DICE: the event's price and status were read, its ticket types weren't |
no_event_date | Tito: the event's calendar file couldn't be read, so results have no date |
list_empty | A page-change list that matched before matched nothing this time |
format_changed | The link answered a PDF last time and a web page now, or the other way round. The result keeps its key, so it reads as a change |
rdap_not_registrable | RDAP: the name is under a registered domain (www.example.com), so it can't be registered itself. The detail names the domain to watch |
rdap_unsupported_tld | RDAP: IANA lists no RDAP server for that ending (.de, .io) |
impact_unknown | Road work: closed_only is set, but the feed marks every event's impact unknown (WSDOT), so closures can't be picked out |
feed_stale | Road work: the feed hasn't been updated in a long time |
hours_not_read | CivicRec facilities: the day is read, but not the hours |
time_zone_default | SimplePractice: the days were picked in New York time because time_zone wasn't set |
price_missing | A rental unit with no rent, or a rent of $0 |
filings_not_found, officers_not_found | Companies House: the company has no filings or no officers on record; the company itself was read |
nothing_listed | Magic Eden: no listings and no floor. A mistyped symbol reads the same way, so check the symbol |
seo_not_for_pdf, seo_not_for_list | seo: true reads a page's tags, so it does nothing on a PDF or a list |
summary_unavailable | summarize: true, but the server has no LLM or the summary failed. It's tried again at most once an hour |
step_failed | A browser step couldn't be done (nothing matched it, or it timed out). The detail names the step |
not_a_page | The link is a file download, not a page; watch it without browser: true |
recipe_review_pending | A reader that waits on a terms review claims this page, so no recipe reads it either |
recipe_input_missing | The recipe needs a value the watcher doesn't give, like location.zip |
recipe_llm_budget | The page needed relearning, but the LLM budget is spent |
recipe_dead | The page was given up on after 3 relearns found nothing. The stored recipe still runs; preview again to retry learning |