# Passage Data > Cited environmental limits for programs and agents. Numeric discharge limits, water, groundwater and air quality guidelines, drinking-water standards and soil and groundwater screening values, read from the regulations themselves: US federal effluent guidelines (40 CFR 133, 405-471), the rules of 31 US states and DC (water quality standards above all), Canadian federal rules and every province's and territory's, German, French, Italian and EU instruments; drinking water from 40 CFR 141 and 143, the EU Directive 2020/2184, Quebec's regulation and the PFAS rules of Germany, France, Italy and Canada; soil and groundwater from EPA's Regional Screening Levels, California's ESLs, Ontario, Quebec, British Columbia, CCME and Italy. Every row cites its instrument, edition, section or page, the quoted text, and the source file's URL and sha256. When nothing is held, the answer is a coded refusal naming every instrument searched, never an empty list. The data is read from public law and public agency files. A refusal is a statement about our sources, not about the law of a place or the protection of a species. Check the cited source before relying on a value. Pages are free; programs get 100 calls a day without a key and $10 of calls a month with a free key (below). We keep what is asked (tool, place, parameter or species name, outcome), the name and version the calling program gives, the network it calls from (operator and country, as Cloudflare reports them) and how many distinct callers a day; never an IP address, an account or a person. build_dwc_archive and audit_archive: A file this tool reads, sent or fetched from the address given, is read as one dropped on our website: what the read finds (column headings, the shape of its rows, the terms they became, refusal codes) is kept so the reader learns, never the file or its values. Names and contacts are dropped or replaced by a code, and protected species' locations are made coarser. check_dmr and fill_dmr are unlike the /dmr/ pages, where nothing typed leaves the browser: the values and rows you send reach our server to be checked or computed and are not kept; the count keeps only how many were sent, flagged, filled and refused, never a value, a unit or a permit number. ## Keys, allowances and prices - The pages are free and need no key: [limits by place](https://passage-data.com/limits/), [US NPDES permits](https://passage-data.com/dmr/). - Without a key: 100 tool calls a UTC day per caller over /api/v1/ and /mcp (search engines' crawlers and an assistant's own fetcher or connector are not counted). Past them a call answers HTTP 429 (over MCP an error result) with `refused.code` `allowance.spent`, `get_key` and `resets`; nothing was run. - A free key: [sign in at https://passage-data.com/?keys](https://passage-data.com/?keys) with an email or Google — no card — and make one under Keys for tools. Send it as `Authorization: Bearer pdk_…` (or `x-api-key: pdk_…`) to https://passage-data.com/api/v1/ and https://passage-data.com/mcp. It carries $10 of calls a month, renewed on the 1st at 00:00 UTC; a call past it answers HTTP 402 `credit.spent` with `balance`. A revoked or mistyped key answers HTTP 401 `key.refused`. A keyed read_certificate never asks for x402 payment. Paid credit is not open yet: nothing is billed. - The month so far: `GET https://passage-data.com/w/credits/balance` with the key: `used_usd`, `left_usd`, `calls`, each tool's calls and dollars, `renews`. - What a call draws on the month (placeholder prices, not final): find_limits, check_value, verify_claim, list_sources, get_permit, find_designations, search, fetch and export_limits $5 per 1,000 calls; list_permits $10 per 1,000; check_dmr and fill_dmr $20 per 1,000; check_values and screen_species $5 per 1,000 rows or names sent; read_certificate and cite_document $0.10 a call; build_dwc_archive, audit_archive, screen_exposure and map_to_darwin_core $0.05 a call; the account tools their measured cost. A refused call draws nothing, and collecting a started reading by its `call_id` is free. ## MCP server - [For a person: add it to an assistant](https://passage-data.com/ai/): the steps for Claude, ChatGPT, Cursor, VS Code, Microsoft 365 Copilot, Claude Code, Codex and Gemini CLI, and the question each tool answers. - [MCP endpoint](https://passage-data.com/mcp): Streamable HTTP; no key for 100 tool calls a day, a free key for more (above). Tools: `find_limits`, `check_value`, `check_values` (a table of values in one call), `read_certificate` (a laboratory certificate's rows, checked), `verify_claim`, `list_sources`, `get_permit`, `list_permits` (a state's permits by EPA's industry listing, in one call), `check_dmr` and `fill_dmr` (a permit's Discharge Monitoring Report: checked, or computed from the plant's rows), `map_to_darwin_core`, `build_dwc_archive` (a species file in, a Darwin Core archive out), `find_designations`, `screen_species` (a species list in one call), `screen_exposure` (occurrence records: is a listed species published at a precision that exposes it), `cite_document` (a public document cited by page), `audit_archive` (a published Darwin Core archive's defects), and `search` and `fetch` (the pair ChatGPT connectors and deep research call), and `export_limits` (what a place's whole table holds; the file itself is licensed). Add it with `claude mcp add --transport http passage-data https://passage-data.com/mcp`, or as a remote connector URL in any MCP client. - Your own sites, signed in: /mcp also lists `list_sites`, `create_site`, `add_document` (a file as base64 or a text, up to 8 MiB, held and read page by page as a file dropped on the site's page) and `ask_site` (a question answered from the site's own documents, every quote found again on its page and cited to the document and page; priced before any call, at most $0.25 a question and $5 a day an account). Called without sign-in they answer HTTP 401 with `WWW-Authenticate: Bearer resource_metadata="https://passage-data.com/.well-known/oauth-protected-resource/mcp"`: a claude.ai or ChatGPT connector then asks you to sign in to Passage Data and allow it (OAuth 2.1: authorization code with PKCE S256, dynamic client registration, refresh tokens; the app is listed on your account page under Apps connected, where you disconnect it). Without sign-in, send the account's key: `claude mcp add --transport http passage-data https://passage-data.com/mcp --header "Authorization: Bearer pdk_…"` (a key made on the account page, Keys for tools), or the same header in Cursor or an SDK client. What a site holds stays in its account: these calls are counted by tool and outcome only. ## HTTP API (JSON; no key for 100 calls a day, a free key for more) - [Service card](https://passage-data.com/api/v1/): what is held (instruments, rows, parameters, places) and every endpoint - [find_limits](https://passage-data.com/api/v1/limits?parameter=ammonia&place=US-OH&medium=surface_water): `parameter` (a name or CAS number) and optionally `place` (ISO 3166: US, US-OH, CA-QC, DE, EU; a place also searches the places above it), `medium` (water_discharge, surface_water, groundwater, drinking_water, soil, ambient_air, air_emission, sediment, leachate), `statistic` (daily_max, monthly_avg, four_day_avg, ...), `kind` (limit, guideline, ...), `max_rows` (1-200, default 50), `offset` - [check_value](https://passage-data.com/api/v1/check?parameter=copper&value=0.15&unit=mg/L&place=CA-QC&medium=water_discharge): `parameter`, `value` (one number), `unit` (mg/L, µg/L, ng/L, mg/kg, µg/m³, pH ...) and the same `place`, `medium`, `statistic`, `max_rows`, `offset`: every row find_limits would return, each with a verdict — over, under, within, or not_comparable with the reason. Units convert exactly within a family; no limit is summed or inferred - [verify_claim](https://passage-data.com/api/v1/verify?parameter=copper&value=0.10&unit=mg/L&place=CA-QC&medium=water_discharge&statistic=monthly_avg): a limit someone stated (in an AI's answer, a report) set beside the rows held: `parameter`, `value`, `unit` and optionally `place`, `medium`, `statistic` (a row code: monthly_avg, daily_max, instant, ...). confirmed (a row prints the same number, in that unit or one that converts exactly, under that statistic, with its cite), contradicted (rows are held and none prints it, each with the number it prints), not_held (a coded refusal naming every instrument searched and its edition: a statement about our sources, not about whether the claim is true), or not_comparable. `POST https://passage-data.com/api/v1/verify` the same as JSON to add the `sentence` the claim came from (up to 2,000 characters): the answer says whether it prints the claim's number and unit; the sentence is never kept - check_values: `POST https://passage-data.com/api/v1/check` with JSON `{"place": "CA-QC", "medium": "water_discharge", "rows": [{"parameter": "copper", "value": 0.15, "unit": "mg/L"}, ...]}` (up to 200 rows; `place` required, `medium`, `statistic`, `max_rows` optional): a lab table in one call, each row answered by check_value itself, in the rows' order. Send a table this way, not one call a row: the door allows 30 calls in 10 seconds - read_certificate: `POST https://passage-data.com/api/v1/certificate` with JSON `{"file_url": "https://.../certificate.pdf", "place": "CA-QC", "medium": "surface_water"}` (or `content_base64` with the bytes, at most 10 MB; or multipart/form-data with the file as `file`): a laboratory certificate — a PDF, or the lab's own XLSX, XLS or CSV export — read by rules into the rows it prints (sample, parameter, value as printed, unit, qualifier, detection limit, page or sheet), and with a `place` each row checked by check_values itself; `exceedances` are the rows over a limit, each limit with its cite. Any other format is refused `format.not_handled` before it is read. The file is not kept - [list_sources](https://passage-data.com/api/v1/sources?place=CA-QC): every instrument held for a place, with edition, rows read and source URL - [export_limits](https://passage-data.com/api/v1/export?place=CA-QC): a place's whole cited table (the place and the places above it) as one file, `format=csv` or `json`, optionally one `medium`. Without a key it says how many rows, by medium and instrument, with the first 20; the whole table goes to a licence key sent as `Authorization: Bearer pdl_...` ([the limits licence](https://passage-data.com/limits/licence)). One parameter at a time stays free above - [get_permit](https://passage-data.com/api/v1/permits/PA0228061): one US NPDES permit from EPA's national file: limits by outfall, renewal due date, the last 12 months' values over a limit - [list_permits](https://passage-data.com/api/v1/permits?state=WI&sic=26): screen a state's NPDES permits by industry in one call (`industry=` words of the SIC description, `sic=` a code or its first digits): each permit's name, SIC, 12-month record (times over a limit, on which parameters), expiry and renewal, and `screen`, the industry's counts in one sentence; narrow with `over_limit=true`, `parameter=phosphorus` or `renewal=due` ([WI paper mills over a limit](https://passage-data.com/api/v1/permits?state=WI&sic=26&over_limit=true)); `upcoming=true` keeps the permits with a new or tighter limit already written in that takes effect later, each with its date and the plant's median today, and `facility_type=potw` or `min_flow_mgd=1` narrow by EPA's facility type and design flow ([OH sewage plants over 1 MGD facing one](https://passage-data.com/api/v1/permits?state=OH&facility_type=potw&min_flow_mgd=1&upcoming=true); every state's at [/dmr/new-limits](https://passage-data.com/dmr/new-limits)) - check_dmr: `POST https://passage-data.com/api/v1/dmr/check` with JSON `{"permit": "PA0228061", "period_end": "2026-09", "values": [{"outfall": "001", "parameter": "00530", "statistic": "MK", "value": 12, "unit": "mg/L"}, ...]}` (up to 500 values; `netdmr: true` adds the NetDMR import file): each value set beside the permit's own limit for the period, from EPA's limits file, with EPA's violation rule's verdict; a value due and not sent is named (filed blank, EPA records it as missing). A parameter is EPA's code or name, or a common one (TSS); a statistic EPA's code (MK, DD, WA) or get_permit's words - fill_dmr: `POST https://passage-data.com/api/v1/dmr/fill` with JSON `{"permit": "PA0228061", "period_end": "2026-09", "rows": [{"date": "2026-09-01", "parameter": "TSS", "value": 12, "unit": "mg/L", "point": "Effluent"}, ...]}` (up to 20,000 rows) or `"table"`: the file's text (a laboratory's long export, or a sheet of days): each value the period's forms ask for (monthly average, daily maximum, weekly average on EPA's Sunday-to-Saturday weeks, loadings, percent removal), the rows it came from and its verdict, or a coded refusal; `nd_rule` (zero, half, dl, dl_lt) sets how a result below detection enters an average, `netdmr: true` adds the NetDMR import file (not yet validated by NetDMR's own import check) - map_to_darwin_core: `POST https://passage-data.com/api/v1/dwc/map` with JSON `{"columns": ["nom_latin", "date", "x", "y"], "sample_rows": [[...], ...]}` (columns: the header row, up to 500; sample_rows optional, up to 400): for each column of a species occurrence or checklist file, the Darwin Core term, the rule that decided it and the alias that matched, or a coded refusal saying why it is unmapped; and the terms GBIF requires of an occurrence or checklist dataset that no column carries - build_dwc_archive: `POST https://passage-data.com/api/v1/dwc/archive` with JSON `{"file_url": "https://.../observations.csv", "metadata": {"title": "...", "abstract": "...", "license": "CC-BY-4.0", "creator": {"given": "...", "sur": "...", "organization": "...", "email": "..."}, "contact": {...}}}`: a species occurrence or checklist file (CSV, TSV, Excel, a zipped shapefile, ...; `file_url` = a public https address the door fetches, or `content_base64` with `filename`; at most 10 MB) or a list of names (`species`, up to 5,000: a checklist) becomes a Darwin Core archive (occurrence.txt or taxon.txt, meta.xml, eml.xml) by the converter behind map_to_darwin_core, which maps every column and checks every value. `core` occurrence or checklist; `chapman_category` (1-4, the data holder's own Chapman 2020 category) coarsens the coordinates of species a conservation register lists and says so in the archive. `columns` ({Darwin Core term: column}) names the column for a term where your user decided; the values are still checked. Over MCP the zip is an embedded resource up to 32 KB; this route returns it whole in `archive.base64`. What the read finds is kept to teach the reader, never the file (the terms above). With `columns_confirmed_by_user: true` (your user confirmed every entry) each confirmed column's words and term, never a value, are also read as a label to teach the converter, said in `learned`. A large file takes up to a minute - [find_designations](https://passage-data.com/api/v1/designations?species=Myotis%20lucifugus&place=CA-ON): `species` (a scientific name, or a common name exactly as a register prints it) and optionally `place` (ISO 3166: US, US-CA, CA-ON, GB-SCT, FR; only the registers that reach it are searched, with IUCN, CITES and, where the country is a Party, CMS). Registers: US Endangered Species Act lists (50 CFR 17.11, 17.12), Canada's Species at Risk Act Schedule 1 and COSEWIC, the provinces' and territories' acts, the IUCN Red List, CITES, CMS, the EU Habitats (Annexes II, IV, V) and Birds (Annex I) Directives, UK Wildlife and Countryside Act and Habitats Regulations schedules - screen_species: `POST https://passage-data.com/api/v1/designations` with JSON `{"place": "CA-ON", "species": ["Myotis lucifugus", "Emydoidea blandingii", ...]}` (up to 500 names; `place` optional): a species list in one call, each name answered by find_designations itself, in the list's order - screen_exposure: `POST https://passage-data.com/api/v1/exposure` with JSON `{"place": "CA-QC", "records": [{"scientificName": "Myotis lucifugus", "decimalLatitude": 45.50173, "decimalLongitude": -73.56731}, ...]}` (up to 5000 records; `place` optional, per record too; a record may also carry `coordinateUncertaintyInMeters`, `dataGeneralizations`, `informationWithheld`, `eventID`, `locationID`, `locality`, and an iNaturalist export's `coordinates_obscured`, `geoprivacy`, `taxon_geoprivacy`, `public_positional_accuracy`; `determinations` optional: the data holder's documented answers to Chapman 2020's questions, `{"1.1": true, ...}`): our engine's names screen (the one behind a file dropped on passage-data.com) says, for each species a register lists, which register lists it and whether the records publish it at a coordinate finer than 0.01 degree (about 1.1 km), and Chapman 2020's treatment. Nothing sent is kept - [search](https://passage-data.com/api/v1/search?query=copper%20effluent%20Quebec): `query`, free text — a substance and a place, a species and a place, an instrument's title, or an NPDES permit number, each read by exact match against what is held. `results`: `id`, `title`, `url` (the page to cite); `searched` says how the query was read and what was read but not held - [fetch](https://passage-data.com/api/v1/fetch?id=limits/CA-QC/copper): `id` from search — `limits//`, `species/[/]`, `place/`, `source/`, `permit/` — as `id`, `title`, `text` (every row with its cite, at most 60 rows, the rest named with their JSON pages), `url` and `metadata` (`json`: the same rows as JSON) - [cite_document](https://passage-data.com/api/v1/cite?url=https%3A%2F%2Fwww.epa.gov%2Fsystem%2Ffiles%2Fdocuments%2F2025-11%2Fnvwqs.pdf&find=Fish%20Muscle%20Tissue%2013.1): `url` (a public http(s) document: PDF, web page or plain text, up to 40 MiB) and `find` (repeat it, at most 10: the numbers or phrases to cite, as the document prints them; `POST https://passage-data.com/api/v1/cite` `{"url", "find": [...]}` the same). `found`: each find with `n_hits` and its first 5 hits, each `page`, `printed`, `quote` and `cite` (page, address, the document's sha256, the day read); `not_found`: each find not printed as typed on the pages read; `kept` (a public document sent here is kept in our library, to be indexed and read into cited rows; a link on a file-sharing host or carrying a signature or token is never kept); `held_as`: the instruments this server reads rows from when the document is one of them - [audit_archive](https://passage-data.com/api/v1/audit?gbif_dataset=ffd67559-7ebe-41cc-9280-74fc40000f01): `gbif_dataset` (a GBIF dataset key: the archive is fetched from the endpoint GBIF's registry lists) or `url` (a Darwin Core archive's address, up to 200 MB), and optionally `annex=true`; or `POST https://passage-data.com/api/v1/audit` with the same as JSON, `content_base64` (the zip itself, up to 10 MB), or multipart with the zip as `file`. The defects the website's own audit finds in it (what the read finds is kept to teach the reader, never the archive: the terms above), each with the check that found it, the file and lines, and its count, and every TDWG BDQ TG2 test, ran or not run with a code. A large archive answers 202 with a `call_id`: `GET https://passage-data.com/api/v1/audit?call_id=...` until it answers 200 - [OpenAPI 3.1 description](https://passage-data.com/openapi.json) ## Answer shape - Each row of `find_limits`: `parameter` (as the instrument prints it), `parameter_key` (cas:… or name:…), `value` (as printed), `value_num`, `range`, `unit`, `statistic`, `direction` (max, min, range), `kind`, `medium`, `place`, `conditions` (as printed), and `cite`: `instrument`, `title`, `issuer`, `edition`, `section`, `page`, `quote`, `source_url`, `source_sha256`; a row read from a register table also gives its `table`, `column` (e.g. `mcl`, `mclg`) and the `note` the register prints beside it. A limit on a SUM (Summe PFAS-4, Sum of PFAS, the 25 PFAS of Health Canada's objective) carries `sum_of`: the `members` its instrument lists, as printed; asking for one member (PFOA, its CAS number, a synonym) finds the sum and the row says it is one. - Each row of `check_value` is a `find_limits` row plus `verdict` (over, under, within, not_comparable), `compared` (your value in the row's unit, `exact`, the limit, its direction) and, when not comparable, `why`: `parameter.named_differently` (a longer name: ask by it), `parameter.member_of_sum` (the row limits a sum the asked substance is one member of: ask by the sum's name with the summed value), `printed_reference`, `condition.dependent` (the number holds only at a stated hardness, pH or temperature), `value.not_a_number` (an equation or narrative), `value.range_of_levels`, `direction.not_printed`, `unit.family_mismatch`, `unit.not_convertible`, `unit.unrecognised`, `unit.refused`, `unit.not_printed`. `counts` and `not_comparable_by` cover every row; rows come over first. A verdict compares numbers: whether your value is the row's statistic and its conditions are yours is for you to say. - `verify_claim` answers `claim` (as read), `verdict` (confirmed, contradicted, not_held, not_comparable), `says`, `counts` (`same`, `differs`, `not_comparable`, over every row), `confirmed_by` (the rows printing the claim's number), `held_otherwise` (rows printing another number, each with `compared.held`, its unit and its own conditions: a row for another kind of facility or another water may be why a claim is contradicted), `not_comparable` (with `why`, as check_value's, plus `value.band` for a row printing a band; a row worked for water of a stated chemistry confirms the same number and never contradicts another), the first 25 rows of each (`listed` says when a list is longer), `other_statistics` when a statistic was given, and `sentence` (`value_printed`, `unit_beside`, `parameter_named`) when one was. Equal is equal as exact decimals: 0.1 = 0.10 = 100 µg/L, and a rounded number is another number. not_held carries `refused`: `place.not_held`, `parameter.unknown`, `rows.none_held` or `statistic.none_held` (with `held_under`), each with `sources_searched`; `statistic.unrecognised` lists the codes. - `check_values`: `summary` (`rows`, `over` = over at least one limit, `under` = under a floor and over none, `within` = within every limit compared, `not_comparable` = limits held and none comparable, `refused`, `refused_by`, `says`); `limits` = each held row once, as find_limits shapes it with its `cite`; `results` = one per row, in order: `index`, `outcome`, and check_value's answer with `verdicts` in place of `rows`, each `{limit, verdict, why?, compared: {value_in_row_unit, exact}}` (`limit` is an index into `limits`: joined, they are check_value's row), or the row's own `refused`. `searched.parameters` says how each parameter was matched; `sources_searched` (when a row is rows.none_held) the instruments searched, once. Refusals of the call: `place.missing`, `place.not_held`, `rows.missing`, `rows.malformed`, `rows.too_many`; of one row also `row.malformed`. - `screen_species`: `summary` (`names`, `listed`, `listed_qualified`, `watch_listed`, `named_not_listed`, `none_held` = named by no register searched, `ambiguous`, `refused`, `listed_by` = names each register lists, `says`); `results` = one per name, in order: `index`, `outcome`, and find_designations' answer (`name`, `designations`, `silent`, or its `refused`). A register's instrument, title, edition, place, note, source URL and sha256 are in `registers` once: an entry or its `cite` leaves out a field only where its value is the register's; a missing `outcome_says` is `outcomes[outcome]`, a missing reconciled `source` is `backbone`. Refusals of the call: `species.missing`, `species.malformed`, `species.too_many`, `place.malformed`. - `read_certificate`: `file` (name, bytes, format, sha256), `read` (lab, certificates, samples, parameters, rows, pages or sheets), `samples` (sample, date, matrix, lab_id, certificate as printed), `rows` (`i`, `s` = index into samples, `parameter` and `value` as printed, `unit`, `qualifier` <, > or ND, `detection_limit`, `rdl`/`mdl`/`loq`, `flag`, `page` or `tab`; with a place `checked_as`, `outcome`, `counts` = check_value's count of the limits held by verdict, `over`/`under` = indexes into `limits`, or `refused`: `value.below_detection`, `value.above_range` and check_value's own codes, a code's sentence once in `refusals`), `exceedances` (every row over or under, whatever its page), `limits` (each find_limits row with its `cite`), `summary` (`samples`, `rows`, `over`, `under`, `within`, `not_comparable`, `below_detection`, `refused`, `says`), `notes` (`page.missing`, `sample.unnamed`, `rows.paged_for_size`, `line.not_carried`), `total`/`next_offset`. Refusals: `file.missing`, `file.ambiguous`, `file.malformed`, `file.too_large`, `file.url_refused`, `file.unreachable`, `format.not_handled`, `certificate.no_rows`, `certificate.not_results`, `read.pending` (with `call_id`), `engine.unreachable`, `engine.busy`. - `check_dmr`: `headline` and `tally` (the forms as filed with exactly the values placed: flagged, undecidable, ok, no_data, not_graded, unreadable, missing, optional), `summary`, `limits` (a table: `fields` once, then one row a value due: limit_value_id, outfall, form, parameter_code, location_code, statistic_code, limit, unit, notes), `names` (parameter, statistic and location names by code), `results` = one per value sent, in order: `index`, `limit` (a row of `limits`), `value`, `verdict`, `says` (none for ok), or its `refused` (`outfall.not_in_permit`, `form.not_in_permit`, `parameter.unrecognised`, `parameter.not_limited`, `location.not_limited`, `statistic.unrecognised`, `statistic.not_limited`, `unit.not_on_form`, `season.not_limited`, `period.not_due` with its reason, `row.ambiguous` with `candidates`, `value.missing`, `value.duplicate`; a refusal without `says` says `refusals_say[code]`), `not_sent` (the due values no row gave), `forms_due`, `forms_not_due`, `due_date`, `source` (EPA's file and its date: cite a limit by its limit_value_id). Refusals of the call: `permit.missing`, `permit.malformed`, `permit.not_held`, `permit.no_limits`, `period.missing`, `period.malformed`, `values.missing`, `values.malformed`, `values.too_many`. - `fill_dmr`: the same `limits`, `names`, `headline`, `tally` and `source`; `values` = one per value due: `limit`, `value` (as the form takes it), `n` (results or days it rests on), `from` (rows of `read.series`), `rows` (where in your input: `read.rows_are`), `nd_rule`, `week_ending`, `verdict`, or its `refused` (`fill.no_column`, `fill.unit`, `fill.ambiguous`, `fill.no_loading_inputs`, `fill.removal_inputs`, `dmr.nd_rule_missing`, `dmr.not_single`, `dmr.window_short`, ...); `read` = how your rows were read: `series` (a table: name, parameter_codes, location_code, unit, outfall, samples, used), `skipped` (each with where and why), `unread` (rows not read, by parameter). Refusals of the call add `rows.missing`, `rows.malformed`, `rows.too_many`, `table.malformed`, `table.too_large`, `nd_rule.malformed`, and the reader's `fill.no_date_column`, `fill.no_parameter_column`. - `screen_exposure`: `summary` (`records`, `with_coordinates`, `names`, `listed` = names listed under a law that applies where the records are, `to_confirm`, `not_asked`, `exposed_species`, `records_exposed` = records of a listed species at a coordinate finer than the 0.01 degree grid that a rounding would hide, `records_not_protectable` = more whose exact position another record states (withhold them), `records_already_declared`, `records_obscured_by_export`, `says` = the screen's own sentence); `exposure` = the screen's exposure as it writes it (`says`, `by_register`, `before` and `after` an example rounding); `species` = each listed species, exposed first: `name` (as sent), `n_records`, `outcome`, `backbone` (GBIF's reading), `listed_by` (`register`, `status`, `outcome`, `applies` = whether the register is law where the records are, `quote` = the line the register prints, when held), `exposure` (`exposed`, `exposed_not_protectable`), `exposed`; `other_names` = [name, records, outcome, why] for names no register lists; `registers` once (instrument, title, edition, source URL, sha256); `searched` (`places`, `registers` = every register screened with its edition); `treatment` = Chapman 2020: with `determinations` reaching a category, its `category`, `grid_degrees`, `georeference`, `coordinates_released`, `dwc_terms`, `rules`, `n_rounded`, `n_withheld`, `data_generalizations` (the wording for the field) and `path`; without, `refused` (`sensitivity.criteria_unevaluated`: a listing is not that determination) with `asks`, every `questions`, the `criteria` and what each of the four `categories` would round and withhold. Refusals of the call: `records.missing`, `records.malformed`, `records.too_many`, `species.missing`, `place.malformed`, `determinations.malformed`, and when the engine cannot answer `engine.timeout`, `engine.unreachable`, `engine.failed`, `engine.busy`, `engine.refused` (its own codes in `engine_codes`), `file.too_large`: nothing was decided. - `searched` says which places and parameter names were matched. `total`, `returned` and `next_offset` page the rows; nothing is cut silently. - Each column of `map_to_darwin_core`: `header`, `status` (mapped, refused, empty, unmapped), `term` and `terms` (Darwin Core names), `rule` (header_exact, header_token, header_packed, header_grid), `evidence` (`alias`, `alias_rank`, `alias_origin`: standard, ours, learned or wikidata; `table`; `also_nominated`), `values_check` (the check the column's values must still pass: not run here), or `refused` (`silence.no_dwc_term_matched`, `silence.unrecognised`, `silence.identifier`, `term.ambiguous_header`, `term.held_by_other_column`, `value.column_empty`, `value.uncorroborated_count`, `value.insufficient`). `required.occurrence` and `required.checklist` list the GBIF-required terms `held` and `missing` (`filled_if_absent` when the converter writes it itself); `questions` names the ties to settle. - `build_dwc_archive`: `archive` (`filename`, `bytes`, `sha256`, `core` = occurrence, taxon or event, `records`, `rows_in`, `rows_lost`, `rows_refused`, `terms`, `extensions`, `members`, `publishable` = `ok` and the metadata GBIF still needs in `missing`, `generalised`, `inline`, and `base64` on this route or an embedded `resource` over MCP — `not_inline` with `archive.too_large_inline` past 32 KB); `could_not_read` (`n`, `items`: each `code`, `scope` = column, row or file, `about`, `n_rows`, `status`, `what_it_means`, `what_we_saw`; the same lines ship in the archive as passage-data-exceptions.csv and .json); `notices` (records of listed species at the precision filed); `says`; `kept`; `learned` (with confirmed columns: `recorded` true, false or null while the read runs, and `says`); `engine` (`seconds`). Refusals: `source.missing`, `source.ambiguous`, `file.ambiguous`, `file.malformed` (content_base64 not base64), `file.too_large`, `file.url_refused` (not https, or a private, loopback or link-local host, the redirects too), `file.unreachable`, `filename.malformed`, `species.malformed`, `species.too_many`, `species.not_occurrences`, `core.malformed`, `chapman_category.malformed`, `metadata.malformed`, `license.unrecognised`, `columns.malformed`, `columns.term_unknown`, `archive.not_built` (the converter's own reason: no species name reached the archive, ...), `archive.priced`, `engine.busy`, `engine.timeout`, `engine.unreachable`. - `refused.code`: `parameter.missing`, `parameter.unknown`, `parameter.ambiguous`, `place.not_held`, `rows.none_held` (with `sources_searched` and `held_elsewhere`), `permit.malformed`, `permit.not_held`; for check_value also `value.missing`, `value.malformed`, `value.ambiguous` (1,500: a comma that reads two ways), `value.negative` (an amount below zero), `value.out_of_range`, `unit.missing`, `unit.refused` (an ambiguous unit such as ppt or gal), `unit.unrecognised`. - Each row of `find_designations`: `register`, `name` (as the register prints it), `status` (as printed), `outcome` (`listed`; `listed_qualified` = listed only under a population or subspecies, with `asks` saying which to confirm; `watch_listed`; `assessed_not_listed`, e.g. IUCN Least Concern), `outcome_says`, `matched_at` (the name as typed, a synonym, a broader taxon, the backbone's lineage), `place`, `legal_instrument`, and `cite` (`instrument`, `title`, `edition`, `quote` = the line the register prints, `source_url`, `source_sha256`). `silent` lists each register searched that does not name the species and what its silence means (`assessed_not_listed`, `out_of_scope`, `scope_undetermined`, `not_assessed`). Refusals: `species.missing`, `species.unreadable`, `species.ambiguous` (with `candidates`), `place.malformed`, `designations.none_held` (with `registers_searched` and their editions). - `audit_archive`: `archive` (`source` gbif, url or upload, `dataset` from GBIF's registry, `bytes`, `sha256`, the `core` and `extensions` its meta.xml declares, the member `read`), `scope` (`rows_read`, `rows_in_file`, `complete`, `columns_read`, `columns_unmapped`, `reads`), `summary` (`checks`, `checks_fired`, `defects`, `bdq_ran`, `bdq_not_compliant`, `bdq_not_run`), `checks` (each of the audit's checks once: `code`, `severity`, `proposes_a_fix`, `findings`, and `what` and `do` for those that fired), `findings` (largest first, at most 100: `check` = an index into `checks`, `code`, `field`, `count`, `of_rows`, `examples`, and `file` with `lines` (at most 5) and `by` (value, row_position, blank_cell, bdq_test, declaration) or `not_located` with why; a `proposal` or the question it `asks`), `bdq` (`ran`: each test with `compliant`, `not_compliant`, `prerequisites_not_met` and, when not compliant, `file`, `lines`, `values`; `not_run` by code: `bdq.terms_absent`, `bdq.authority_absent`, `bdq.does_not_reproduce`, `bdq.not_implemented`), `kept`, and `annex` (`text`: markdown) when asked. A pending audit is `{pending, call_id}`. Refusals: `source.missing`, `source.several`, `dataset.malformed`, `dataset.not_found`, `dataset.no_archive`, `registry.unreachable`, `url.malformed`, `url.refused` (not http(s), or a host that is not public), `archive.unreachable`, `archive.too_large`, `archive.not_dwca`, `archive.unreadable`, `call_id.malformed`, `call_id.unknown`, `file.malformed`, `engine.*`. ## For people - [Cited limits by place](https://passage-data.com/limits/): the same rows as pages, one per place and parameter, e.g. [copper in Québec](https://passage-data.com/limits/CA-QC/copper) or [ammonia in Ohio](https://passage-data.com/limits/US-OH/ammonia); each page links its JSON. [Sitemap](https://passage-data.com/sitemap-limits.xml) - [NPDES permits by state](https://passage-data.com/dmr/p/): every active individual US discharge permit, its limits and EPA's 12-month record - [Passage Data](https://passage-data.com/): drop a site's documents; get what each says, cited to the page