@usa-open-data-connectors/usa-sources 0.1.3 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +575 -214
  3. package/dist/apiKey.d.ts +12 -0
  4. package/dist/apiKey.d.ts.map +1 -0
  5. package/dist/apiKey.js +18 -0
  6. package/dist/apiKey.js.map +1 -0
  7. package/dist/blsSeries.d.ts.map +1 -1
  8. package/dist/blsSeries.js +2 -4
  9. package/dist/blsSeries.js.map +1 -1
  10. package/dist/btsTransportationStats.d.ts.map +1 -1
  11. package/dist/btsTransportationStats.js +3 -5
  12. package/dist/btsTransportationStats.js.map +1 -1
  13. package/dist/callookCallsigns.d.ts +119 -0
  14. package/dist/callookCallsigns.d.ts.map +1 -0
  15. package/dist/callookCallsigns.js +264 -0
  16. package/dist/callookCallsigns.js.map +1 -0
  17. package/dist/cdcCountyObesity.d.ts.map +1 -1
  18. package/dist/cdcCountyObesity.js +3 -5
  19. package/dist/cdcCountyObesity.js.map +1 -1
  20. package/dist/cdcSocrataCatalogue.d.ts.map +1 -1
  21. package/dist/cdcSocrataCatalogue.js +3 -5
  22. package/dist/cdcSocrataCatalogue.js.map +1 -1
  23. package/dist/cfpbConsumerComplaints.d.ts +2 -0
  24. package/dist/cfpbConsumerComplaints.d.ts.map +1 -1
  25. package/dist/cfpbConsumerComplaints.js +12 -12
  26. package/dist/cfpbConsumerComplaints.js.map +1 -1
  27. package/dist/clinicalTrialsStudies.d.ts.map +1 -1
  28. package/dist/clinicalTrialsStudies.js +3 -5
  29. package/dist/clinicalTrialsStudies.js.map +1 -1
  30. package/dist/collegeScorecardInstitutions.d.ts +133 -0
  31. package/dist/collegeScorecardInstitutions.d.ts.map +1 -0
  32. package/dist/collegeScorecardInstitutions.js +339 -0
  33. package/dist/collegeScorecardInstitutions.js.map +1 -0
  34. package/dist/cpscProductRecalls.d.ts.map +1 -1
  35. package/dist/cpscProductRecalls.js +2 -4
  36. package/dist/cpscProductRecalls.js.map +1 -1
  37. package/dist/epaEnvirofactsFacilities.d.ts.map +1 -1
  38. package/dist/epaEnvirofactsFacilities.js +2 -4
  39. package/dist/epaEnvirofactsFacilities.js.map +1 -1
  40. package/dist/errors.d.ts +12 -2
  41. package/dist/errors.d.ts.map +1 -1
  42. package/dist/errors.js +8 -4
  43. package/dist/errors.js.map +1 -1
  44. package/dist/estimatetaxIncomeTax.d.ts +140 -0
  45. package/dist/estimatetaxIncomeTax.d.ts.map +1 -0
  46. package/dist/estimatetaxIncomeTax.js +225 -0
  47. package/dist/estimatetaxIncomeTax.js.map +1 -0
  48. package/dist/fdicBankDirectory.d.ts.map +1 -1
  49. package/dist/fdicBankDirectory.js +2 -4
  50. package/dist/fdicBankDirectory.js.map +1 -1
  51. package/dist/femaDisasterDeclarations.d.ts.map +1 -1
  52. package/dist/femaDisasterDeclarations.js +2 -4
  53. package/dist/femaDisasterDeclarations.js.map +1 -1
  54. package/dist/healthdataSocrataCatalogue.d.ts.map +1 -1
  55. package/dist/healthdataSocrataCatalogue.js +3 -5
  56. package/dist/healthdataSocrataCatalogue.js.map +1 -1
  57. package/dist/http.d.ts +40 -0
  58. package/dist/http.d.ts.map +1 -0
  59. package/dist/http.js +93 -0
  60. package/dist/http.js.map +1 -0
  61. package/dist/index.d.ts +33 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +17 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/ncbiPubmedSearch.d.ts +4 -0
  66. package/dist/ncbiPubmedSearch.d.ts.map +1 -1
  67. package/dist/ncbiPubmedSearch.js +10 -4
  68. package/dist/ncbiPubmedSearch.js.map +1 -1
  69. package/dist/nceiAnnualTemperature.d.ts.map +1 -1
  70. package/dist/nceiAnnualTemperature.js +3 -5
  71. package/dist/nceiAnnualTemperature.js.map +1 -1
  72. package/dist/nextBusTransitVehicles.d.ts +102 -0
  73. package/dist/nextBusTransitVehicles.d.ts.map +1 -0
  74. package/dist/nextBusTransitVehicles.js +171 -0
  75. package/dist/nextBusTransitVehicles.js.map +1 -0
  76. package/dist/noaaSeaLevel.d.ts.map +1 -1
  77. package/dist/noaaSeaLevel.js +2 -4
  78. package/dist/noaaSeaLevel.js.map +1 -1
  79. package/dist/nwsPointForecast.d.ts +1 -1
  80. package/dist/nwsPointForecast.d.ts.map +1 -1
  81. package/dist/nwsPointForecast.js +7 -6
  82. package/dist/nwsPointForecast.js.map +1 -1
  83. package/dist/nycOpenDataCatalogue.d.ts +97 -0
  84. package/dist/nycOpenDataCatalogue.d.ts.map +1 -0
  85. package/dist/nycOpenDataCatalogue.js +151 -0
  86. package/dist/nycOpenDataCatalogue.js.map +1 -0
  87. package/dist/openFdaFoodRecalls.d.ts.map +1 -1
  88. package/dist/openFdaFoodRecalls.js +2 -4
  89. package/dist/openFdaFoodRecalls.js.map +1 -1
  90. package/dist/publicDataHubHospital.d.ts +116 -0
  91. package/dist/publicDataHubHospital.d.ts.map +1 -0
  92. package/dist/publicDataHubHospital.js +175 -0
  93. package/dist/publicDataHubHospital.js.map +1 -0
  94. package/dist/registry.d.ts.map +1 -1
  95. package/dist/registry.js +16 -2
  96. package/dist/registry.js.map +1 -1
  97. package/dist/secEdgarFilings.d.ts +1 -1
  98. package/dist/secEdgarFilings.d.ts.map +1 -1
  99. package/dist/secEdgarFilings.js +7 -6
  100. package/dist/secEdgarFilings.js.map +1 -1
  101. package/dist/treasuryAvgInterestRate.d.ts.map +1 -1
  102. package/dist/treasuryAvgInterestRate.js +2 -4
  103. package/dist/treasuryAvgInterestRate.js.map +1 -1
  104. package/dist/treasuryDebtToPenny.d.ts.map +1 -1
  105. package/dist/treasuryDebtToPenny.js +2 -4
  106. package/dist/treasuryDebtToPenny.js.map +1 -1
  107. package/dist/usaspendingAgencies.d.ts.map +1 -1
  108. package/dist/usaspendingAgencies.js +3 -5
  109. package/dist/usaspendingAgencies.js.map +1 -1
  110. package/dist/usgsEarthquakes.d.ts.map +1 -1
  111. package/dist/usgsEarthquakes.js +2 -4
  112. package/dist/usgsEarthquakes.js.map +1 -1
  113. package/dist/usgsPeakStreamflow.d.ts.map +1 -1
  114. package/dist/usgsPeakStreamflow.js +3 -5
  115. package/dist/usgsPeakStreamflow.js.map +1 -1
  116. package/package.json +7 -4
package/README.md CHANGED
@@ -1,30 +1,70 @@
1
1
  # @usa-open-data-connectors/usa-sources
2
2
 
3
- Uniform TypeScript adapters for US public data sources. Keyless first, with a
4
- strict parser and a committed fixture behind every adapter so a build never
5
- depends on a live API.
6
-
7
- The HTTP API and the CLI read this package's registry (`US_DATA_SOURCES`), so a
8
- new adapter is exposed on `/api/sources`, `/api/sources/{id}/probe`, and
9
- `/api/sources/{id}/data` as soon as it is registered.
10
-
11
- ## Sources
12
-
13
- | id | publisher | auth | what it reads |
14
- | ---------------------------- | --------------------------------------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------- |
15
- | `bls-unemployment-rate` | Bureau of Labor Statistics | none | The national unemployment rate, monthly, seasonally adjusted |
16
- | `usgs-hawaii-earthquakes` | US Geological Survey | none | Earthquakes of magnitude 2.5 and above near the Hawaiian islands |
17
- | `cdc-county-obesity` | Centers for Disease Control (CDC) | none | The share of adults with obesity in each US county, from CDC PLACES |
18
- | `ncei-annual-temperature` | NOAA National Centers for Environmental Information | none | Calendar-year average temperature for the contiguous United States, since 1895 |
19
- | `noaa-sea-level` | NOAA Center for Operational Oceanographic Products and Services | none | Monthly mean sea level at a tide gauge, folded into calendar-year averages, since 1856 at The Battery |
20
- | `treasury-avg-interest-rate` | US Department of the Treasury | none | Average interest rate on the interest-bearing federal debt outstanding, monthly since 2001 |
21
- | `fema-disaster-declarations` | Federal Emergency Management Agency | none | Every disaster declaration FEMA has published, one row per declaration, from 1953 to the newest one |
22
- | `openfda-food-recalls` | US Food and Drug Administration | none | Every food recall FDA has published as an enforcement report, from June 2012 to the newest publication date |
23
- | `usgs-peak-streamflow` | US Geological Survey | none | The annual peak-flow record at a USGS stream gauge, one row per water year, since 1844 at the Mississippi River at St. Louis |
24
- | `cpsc-product-recalls` | US Consumer Product Safety Commission | none | Every consumer product recall CPSC has published, one row per recall, from 2014 to the newest one |
25
- | `cfpb-consumer-complaints` | Consumer Financial Protection Bureau | none | Every consumer complaint sent to a company since December 2011, counted by year, with the products and companies named most |
26
-
27
- ## Usage
3
+ Uniform TypeScript adapters for US public data sources, with live probes and offline fixtures.
4
+
5
+ ## What this package does
6
+
7
+ - Exposes 26 source adapters behind one interface.
8
+ - Fetches live data from each source and parses it into plain objects.
9
+ - Loads a committed fixture when the network is unavailable.
10
+ - Publishes the `US_DATA_SOURCES` registry for the API and the CLI.
11
+ - Exports named functions for each source, with that source's query parameters.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ npm install @usa-open-data-connectors/usa-sources
17
+ ```
18
+
19
+ ## Quick start
20
+
21
+ ```ts
22
+ import { probeAllUsDataSources } from '@usa-open-data-connectors/usa-sources';
23
+
24
+ const probes = await probeAllUsDataSources();
25
+ console.log(probes.map((probe) => `${probe.id}: ${probe.ok ? 'ok' : probe.status}`).join('\n'));
26
+ ```
27
+
28
+ ## Adapters
29
+
30
+ | Adapter | Publisher | What it reads |
31
+ | --- | --- | --- |
32
+ | `bls-unemployment-rate` | Bureau of Labor Statistics | Monthly national unemployment rate, seasonally adjusted, from the Current Population Survey. |
33
+ | `usgs-hawaii-earthquakes` | US Geological Survey | Earthquakes of magnitude 2.5 and above near the Hawaiian islands. |
34
+ | `cdc-county-obesity` | Centers for Disease Control and Prevention | Model-based share of adults with obesity in each US county, from CDC PLACES. |
35
+ | `ncei-annual-temperature` | NOAA National Centers for Environmental Information | Calendar-year average temperature for the contiguous United States since 1895. |
36
+ | `noaa-sea-level` | NOAA Center for Operational Oceanographic Products and Services | Monthly mean sea level at a tide gauge, folded into calendar-year averages with a fitted trend. |
37
+ | `treasury-avg-interest-rate` | US Department of the Treasury | Average interest rate on the interest-bearing federal debt outstanding, monthly since 2001. |
38
+ | `fema-disaster-declarations` | Federal Emergency Management Agency | Every disaster declaration FEMA has published, from 1953 to the newest. |
39
+ | `openfda-food-recalls` | US Food and Drug Administration | Every food recall the FDA publishes as an enforcement report, from June 2012. |
40
+ | `usgs-peak-streamflow` | US Geological Survey | Annual peak-flow record at a USGS stream gauge, from 1844 at the Mississippi River at St. Louis. |
41
+ | `cpsc-product-recalls` | US Consumer Product Safety Commission | Every consumer product recall CPSC has published, from 2014 to the newest. |
42
+ | `cfpb-consumer-complaints` | Consumer Financial Protection Bureau | Every consumer complaint sent to a company, counted by year, with the top products and companies. |
43
+ | `treasury-debt-to-penny` | US Department of the Treasury | Daily total public debt outstanding, in dollars, from the debt to the penny file. |
44
+ | `sec-edgar-filings` | US Securities and Exchange Commission | An issuer's recent filing history from SEC EDGAR, with the form type and the newest date. |
45
+ | `fdic-bank-directory` | Federal Deposit Insurance Corporation | Insured banks in the FDIC institution directory, with the city and state of each main office. |
46
+ | `clinicaltrials-studies` | ClinicalTrials.gov | Registered clinical studies, with the status, phase, lead sponsor, and enrollment. |
47
+ | `nws-point-forecast` | National Weather Service | The forecast grid point for a latitude and longitude, with the URLs for the daily and hourly forecast. |
48
+ | `usaspending-agencies` | USAspending | Top-tier federal agencies, with budget authority, obligations, and outlays for the active quarter. |
49
+ | `epa-envirofacts-facilities` | Environmental Protection Agency | Toxics Release Inventory facilities, with county, address, and open or closed status. |
50
+ | `ncbi-pubmed-search` | National Center for Biotechnology Information | PubMed literature search results, with the match count and a page of PubMed ids. |
51
+ | `cdc-socrata-catalogue` | Centers for Disease Control and Prevention | The CDC Socrata catalogue, one row per dataset, chart, or story. |
52
+ | `healthdata-socrata-catalogue` | HealthData.gov | The HealthData.gov Socrata catalogue, one row per dataset, chart, or story. |
53
+ | `bts-transportation-stats` | Bureau of Transportation Statistics | The BTS Socrata catalogue, one row per dataset, chart, or story. |
54
+ | `estimatetax-income-tax` | EstimateTax | US federal, state, and local income tax for one salary, with the official document behind every figure. |
55
+ | `nyc-open-data-catalogue` | New York City Open Data | The NYC Socrata catalogue, one row per published dataset or map. |
56
+ | `callook-callsigns` | Callook.info | US amateur radio callsign records from the FCC licence file, with the licence class, location, and expiry. |
57
+ | `college-scorecard-institutions` | College Scorecard, US Department of Education | Institution-level enrollment, cost, completion, debt, and earnings ten years after entry for US colleges. |
58
+ | `nextbus-transit-vehicles` | Portland Streetcar, through the NextBus XML feed | Live positions, headings, speeds, and report ages for the streetcars on the NS line. |
59
+
60
+ The HTTP API and the CLI read this package's registry. A new adapter is exposed on
61
+ `/api/sources`, `/api/sources/{id}/probe`, and `/api/sources/{id}/data` as soon as it is registered.
62
+
63
+ ### Usage examples
64
+
65
+ The examples below hit the live sources and need no key. Each one uses the exported functions for that adapter.
66
+
67
+ `bls-unemployment-rate`:
28
68
 
29
69
  ```ts
30
70
  import { fetchBlsSeries, US_UNEMPLOYMENT_SERIES_ID } from '@usa-open-data-connectors/usa-sources';
@@ -36,6 +76,8 @@ const rate = await fetchBlsSeries(US_UNEMPLOYMENT_SERIES_ID, {
36
76
  console.log(rate.latest.year, rate.latest.period, rate.latest.value);
37
77
  ```
38
78
 
79
+ `usgs-hawaii-earthquakes`:
80
+
39
81
  ```ts
40
82
  import {
41
83
  buildUsgsEarthquakeCatalogue,
@@ -54,6 +96,8 @@ const catalogue = buildUsgsEarthquakeCatalogue(earthquakes);
54
96
  console.log(catalogue.count, catalogue.strongest.place, catalogue.strongest.magnitude);
55
97
  ```
56
98
 
99
+ `cdc-county-obesity`:
100
+
57
101
  ```ts
58
102
  import {
59
103
  buildCdcCountyObesitySet,
@@ -64,6 +108,8 @@ const set = buildCdcCountyObesitySet(await fetchCdcCountyObesity());
64
108
  console.log(set.countyCount, set.lowest.countyName, set.highest.percent, set.national.percent);
65
109
  ```
66
110
 
111
+ `ncei-annual-temperature`:
112
+
67
113
  ```ts
68
114
  import {
69
115
  buildNceiAnnualTemperatureSeries,
@@ -77,6 +123,8 @@ const series = await fetchNceiAnnualTemperature({
77
123
  console.log(series.yearCount, series.warmest.year, series.coldest.valueFahrenheit);
78
124
  ```
79
125
 
126
+ `noaa-sea-level`:
127
+
80
128
  ```ts
81
129
  import { buildNoaaSeaLevelSeries, fetchNoaaSeaLevel } from '@usa-open-data-connectors/usa-sources';
82
130
 
@@ -89,6 +137,8 @@ console.log(
89
137
  );
90
138
  ```
91
139
 
140
+ `treasury-avg-interest-rate`:
141
+
92
142
  ```ts
93
143
  import { fetchTreasuryAvgInterestRates } from '@usa-open-data-connectors/usa-sources';
94
144
 
@@ -96,6 +146,189 @@ const rates = await fetchTreasuryAvgInterestRates();
96
146
  console.log(rates.monthCount, rates.lastMonth.averageInterestRatePercent, rates.lowest.recordDate);
97
147
  ```
98
148
 
149
+ `treasury-debt-to-penny`:
150
+
151
+ ```ts
152
+ import {
153
+ buildTreasuryDebtToPennyPage,
154
+ fetchTreasuryDebtToPenny,
155
+ } from '@usa-open-data-connectors/usa-sources';
156
+
157
+ const page = buildTreasuryDebtToPennyPage(
158
+ (await fetchTreasuryDebtToPenny({ rowLimit: 10_000 })).days
159
+ );
160
+ console.log(
161
+ page.dayCount,
162
+ page.firstDay.recordDate,
163
+ page.lastDay.recordDate,
164
+ page.lastDay.totalPublicDebtOutstandingUsd
165
+ );
166
+ ```
167
+
168
+ `fdic-bank-directory`:
169
+
170
+ ```ts
171
+ import {
172
+ buildFdicBankDirectoryUrl,
173
+ parseFdicBankDirectoryPayload,
174
+ } from '@usa-open-data-connectors/usa-sources';
175
+
176
+ const url = new URL(
177
+ buildFdicBankDirectoryUrl({ rowLimit: 10_000, fields: 'ID,NAME,CITY,STALP' })
178
+ );
179
+ url.searchParams.set('filters', 'ACTIVE:1');
180
+ const response = await fetch(url);
181
+ if (!response.ok) {
182
+ throw new Error(`HTTP ${response.status} from ${url.toString()}`);
183
+ }
184
+ const directory = parseFdicBankDirectoryPayload(await response.json());
185
+ console.log(directory.bankCount, directory.totalBanks, directory.states[0]);
186
+ ```
187
+
188
+ The public API is `FDIC_BANK_API_BASE`, `FDIC_BANK_ROW_LIMIT`,
189
+ `FDIC_BANK_FIELDS`, `buildFdicBankDirectoryUrl`,
190
+ `parseFdicBankDirectoryPayload`, `fetchFdicBankDirectory`, and
191
+ `fdicBankDirectoryAdapter`, with the `FdicBank`, `FdicBankStateCount`, and
192
+ `FdicBankDirectory` types. The parser validates the response with a zod schema:
193
+ `meta.total`, `totals.count`, `data[].data.{ID,NAME,CITY,STALP}`, and
194
+ `errors`. A row missing any of the four fields throws `UsSourceParseError`; a
195
+ body with an `errors` array throws `UsSourceApiError`.
196
+
197
+ `sec-edgar-filings`:
198
+
199
+ ```ts
200
+ import {
201
+ fetchSecEdgarFilings,
202
+ SEC_EDGAR_APPLE_CIK,
203
+ } from '@usa-open-data-connectors/usa-sources';
204
+
205
+ const company = await fetchSecEdgarFilings({ cik: SEC_EDGAR_APPLE_CIK });
206
+ console.log(
207
+ company.name,
208
+ company.filingCount,
209
+ company.newestFilingDate,
210
+ company.formCounts[0]
211
+ );
212
+ ```
213
+
214
+ The public API is `SEC_EDGAR_SUBMISSIONS_API_BASE`, `SEC_EDGAR_APPLE_CIK`,
215
+ `SEC_EDGAR_USER_AGENT`, `buildSecEdgarSubmissionsUrl`,
216
+ `parseSecEdgarFilingsPayload`, `fetchSecEdgarFilings`, and
217
+ `secEdgarFilingsAdapter`, with the `SecEdgarFiling`, `SecEdgarFormCount`, and
218
+ `SecEdgarCompanyFilings` types. The parser validates the response with a zod
219
+ schema over `cik`, `name`, `tickers`, `exchanges`, `fiscalYearEnd`, and
220
+ `filings.recent`. The `recent` block sends parallel arrays for the accession
221
+ number, filing date, report date, form, size, XBRL flags, primary document,
222
+ and description. The parser zips them into one row per filing and throws
223
+ `UsSourceParseError` when the arrays do not line up or a filing date is not
224
+ `YYYY-MM-DD`.
225
+
226
+ `epa-envirofacts-facilities`:
227
+
228
+ ```ts
229
+ import {
230
+ buildEpaEnvirofactsFacilitiesUrl,
231
+ parseEpaEnvirofactsFacilitiesPayload,
232
+ } from '@usa-open-data-connectors/usa-sources';
233
+
234
+ const url = buildEpaEnvirofactsFacilitiesUrl({ stateAbbr: 'CA', rowWindow: '0:9999' });
235
+ const response = await fetch(url);
236
+ if (!response.ok) {
237
+ throw new Error(`HTTP ${response.status} from ${url}`);
238
+ }
239
+ const page = parseEpaEnvirofactsFacilitiesPayload(await response.json());
240
+ console.log(page.facilityCount, page.openCount, page.counties[0]);
241
+ ```
242
+
243
+ The public API is `EPA_ENVIROFACTS_API_BASE`, `EPA_ENVIROFACTS_TABLE`,
244
+ `EPA_ENVIROFACTS_STATE_COLUMN`, `EPA_ENVIROFACTS_STATE_ABBR`,
245
+ `EPA_ENVIROFACTS_ROW_WINDOW`, `buildEpaEnvirofactsFacilitiesUrl`,
246
+ `parseEpaEnvirofactsFacilitiesPayload`, `fetchEpaEnvirofactsFacilities`, and
247
+ `epaEnvirofactsFacilitiesAdapter`, with the `EpaEnvirofactsFacility`,
248
+ `EpaEnvirofactsCountyCount`, and `EpaEnvirofactsFacilityPage` types. The
249
+ parser validates a bare JSON array against a zod schema over
250
+ `tri_facility_id`, `facility_name`, `state_abbr`, and the optional
251
+ `street_address`, `city_name`, `county_name`, `zip_code`, `region`,
252
+ `fac_closed_ind`, and `parent_co_name` fields. A rejected query answers with
253
+ `{error}` rather than an array, which throws `UsSourceApiError`.
254
+
255
+ `cdc-socrata-catalogue`:
256
+
257
+ ```ts
258
+ import {
259
+ buildCdcSocrataCatalogueUrl,
260
+ parseCdcSocrataCataloguePayload,
261
+ } from '@usa-open-data-connectors/usa-sources';
262
+
263
+ const url = buildCdcSocrataCatalogueUrl({ limit: 10_000 });
264
+ const response = await fetch(url);
265
+ if (!response.ok) {
266
+ throw new Error(`HTTP ${response.status} from ${url}`);
267
+ }
268
+ const catalogue = parseCdcSocrataCataloguePayload(await response.json());
269
+ console.log(catalogue.entryCount, catalogue.datasetCount, catalogue.categories[0]);
270
+ ```
271
+
272
+ The public API is `CDC_SOCRATA_CATALOGUE_API_BASE`,
273
+ `CDC_SOCRATA_CATALOGUE_LIMIT`, `buildCdcSocrataCatalogueUrl`,
274
+ `parseCdcSocrataCataloguePayload`, `fetchCdcSocrataCatalogue`, and
275
+ `cdcSocrataCatalogueAdapter`, with the `CdcSocrataCatalogueEntry`,
276
+ `CdcSocrataCategoryCount`, and `CdcSocrataCatalogue` types. The parser
277
+ validates a bare JSON array against a zod schema over `id`, `name`,
278
+ `assetType`, `category`, `viewType`, `createdAt`, `viewLastModified`,
279
+ `viewCount`, and `downloadCount`. A row missing `id`, `name`, or `createdAt`
280
+ throws `UsSourceParseError`. A response with no rows also throws, because an
281
+ empty catalogue is not a usable answer.
282
+
283
+ `bts-transportation-stats`:
284
+
285
+ ```ts
286
+ import {
287
+ buildBtsTransportationStatsUrl,
288
+ parseBtsTransportationStatsPayload,
289
+ } from '@usa-open-data-connectors/usa-sources';
290
+
291
+ const url = buildBtsTransportationStatsUrl({ limit: 10_000 });
292
+ const response = await fetch(url);
293
+ if (!response.ok) {
294
+ throw new Error(`HTTP ${response.status} from ${url}`);
295
+ }
296
+ const catalogue = parseBtsTransportationStatsPayload(await response.json());
297
+ console.log(catalogue.entryCount, catalogue.datasetCount, catalogue.categories[0]);
298
+ ```
299
+
300
+ The public API is `BTS_TRANSPORTATION_STATS_API_BASE`,
301
+ `BTS_TRANSPORTATION_STATS_LIMIT`, `buildBtsTransportationStatsUrl`,
302
+ `parseBtsTransportationStatsPayload`, `fetchBtsTransportationStats`, and
303
+ `btsTransportationStatsAdapter`, with the `BtsCatalogueEntry`,
304
+ `BtsCatalogueCategoryCount`, and `BtsTransportationCatalogue` types. The parser
305
+ validates a bare JSON array against a zod schema over `id`, `name`,
306
+ `assetType`, `category`, `viewType`, `createdAt`, `viewLastModified`,
307
+ `viewCount`, and `downloadCount`. A row missing `id`, `name`, or `createdAt`
308
+ throws `UsSourceParseError`. A response with no rows also throws, because an
309
+ empty catalogue is not a usable answer.
310
+
311
+ `nextbus-transit-vehicles`:
312
+
313
+ ```ts
314
+ import { fetchNextBusVehicles } from '@usa-open-data-connectors/usa-sources';
315
+
316
+ const snapshot = await fetchNextBusVehicles();
317
+ console.log(snapshot.vehicleCount, snapshot.vehicles[0]);
318
+ ```
319
+
320
+ The public API is `NEXTBUS_XML_FEED_BASE`, `NEXTBUS_PORTLAND_STREETCAR_AGENCY_TAG`,
321
+ `NEXTBUS_PORTLAND_STREETCAR_ROUTE_TAG`, `NEXTBUS_PORTLAND_STREETCAR_QUERY`,
322
+ `buildNextBusVehicleUrl`, `parseNextBusVehiclePayload`, `fetchNextBusVehicles`,
323
+ and `nextBusTransitVehiclesAdapter`, with the `NextBusVehicle`,
324
+ `NextBusVehicleQuery`, and `NextBusVehicleSnapshot` types. The parser reads
325
+ the XML attributes as strings and validates every field with zod. An error
326
+ body, a missing timestamp, or a position outside the map throws
327
+ `UsSourceParseError`. A route with no vehicles is valid and returns an empty
328
+ `vehicles` list.
329
+
330
+ `fema-disaster-declarations`:
331
+
99
332
  ```ts
100
333
  import { fetchFemaDeclarations } from '@usa-open-data-connectors/usa-sources';
101
334
 
@@ -107,6 +340,8 @@ console.log(
107
340
  );
108
341
  ```
109
342
 
343
+ `usgs-peak-streamflow`:
344
+
110
345
  ```ts
111
346
  import {
112
347
  buildUsgsPeakStreamflowSeries,
@@ -122,6 +357,8 @@ console.log(
122
357
  );
123
358
  ```
124
359
 
360
+ `cpsc-product-recalls`:
361
+
125
362
  ```ts
126
363
  import { fetchCpscProductRecalls } from '@usa-open-data-connectors/usa-sources';
127
364
 
@@ -134,6 +371,8 @@ console.log(
134
371
  );
135
372
  ```
136
373
 
374
+ `cfpb-consumer-complaints`:
375
+
137
376
  ```ts
138
377
  import { fetchCfpConsumerComplaints } from '@usa-open-data-connectors/usa-sources';
139
378
 
@@ -146,194 +385,316 @@ console.log(
146
385
  );
147
386
  ```
148
387
 
149
- ## Notes on the BLS API
150
-
151
- - The public API refuses a request spanning more than ten years, so
152
- `fetchBlsSeries` splits a longer range into windows: two decades cost three
153
- calls once the range runs into the current year.
154
- - Keyless access allows 25 series queries a day. A registered key raises that
155
- to 500, and the adapter takes `apiKey` for the day that matters.
156
- - The agency uses `-` for a month it could not publish, and `M13` for an
157
- annual average. Both are dropped, so one year holds twelve monthly values.
158
-
159
- ## Notes on the USGS earthquake catalogue
160
-
161
- - The event query answers a window of any length, one request per window. The
162
- box, the magnitude floor, and the dates come from the caller.
163
- - The feed mixes tectonic earthquakes with quarry blasts, explosions, and ice
164
- quakes, and a row can arrive without a magnitude. The parser keeps only
165
- tectonic earthquakes that carry a magnitude and a depth.
166
- - The catalogue answers newest first. `parseUsgsEarthquakes` returns oldest
167
- first, so a caller reading index 0 gets the start of the window.
168
-
169
- ## Notes on the CDC PLACES county release
170
-
171
- - The resource id is per release. `cdc-county-obesity` reads the 2025
172
- release, whose estimates come from the 2023 Behavioral Risk Factor
173
- Surveillance System (BRFSS) and the Census Bureau's 2023 county population
174
- estimates. A new release gets a new id, so a caller pinned to this one
175
- keeps reading the numbers it was written against.
176
- - Every row is a model-based estimate, not a count. The release publishes
177
- each measure twice, as crude and as age-adjusted prevalence; the adapter
178
- reads the crude rows, because age-adjusted ones answer a different question.
179
- - A county the model could not estimate arrives with no value, and the
180
- release carries no obesity rows at all for Kentucky and Pennsylvania. Rows
181
- without a value are dropped, and the national row is kept apart from the
182
- counties rather than counted as one.
183
-
184
- ## Notes on the Climate at a Glance download
185
-
186
- - The download is a CSV, not JSON: two comment lines, a header, then one row
187
- per year as `YYYYMM,value` in degrees Fahrenheit. `parseNceiAnnualTemperatureCsv`
188
- skips the comments and the header, and stops on any other line shape rather
189
- than dropping it.
190
- - The URL carries a window length and a window end month. Twelve months ending
191
- in December is the calendar year, which is the number a story about a year
192
- can compare. A shorter window ending in December would be that month alone,
193
- and the parser refuses a file with more than one row per year.
194
- - The current year joins the file only once December has closed it, so the
195
- newest row is the last complete calendar year. The 2026 download is dated
196
- 2026-09-26 and ends at 2025.
197
-
198
- ## Notes on the CO-OPS sea level record
199
-
200
- - One request covers the whole window: the `monthly_mean` product answers one
201
- row per month rather than one row per reading, so 170 years cost one call
202
- and about 460 KB. The endpoint is keyless.
203
- - Values are metres against the station's MSL datum, the average of hourly
204
- heights over the 1983-2001 National Tidal Datum Epoch. Reading the whole
205
- record against one fixed epoch is what makes two years comparable.
206
- - The API answers a row for every month in the window and leaves the value
207
- blank when it has none, which is how the early gaps arrive (1920 holds
208
- seven months, 1861 and 1879-1892 hold none). Blank rows are dropped, and a
209
- year's average is taken over the months it does have, so `monthCount` says
210
- how much of the year a value covers.
211
- - Errors come back in the body with HTTP 200, as
212
- `{"error":{"message":"..."}}`, so the parser checks for that before reading
213
- the data. A wrong product name answers with plain text instead, which fails
214
- the shape check.
215
-
216
- ## Notes on the Treasury average interest rate
217
-
218
- - One keyless request covers the whole run. The dataset answers a row per
219
- month per security type, and the filter pins the response to
220
- `security_type_desc:eq:Interest-bearing Debt`, which is the portfolio total
221
- the bureau's own monthly statement is built from. The other rows, the rates
222
- on bills, notes, bonds, and non-marketable debt, never reach the parser.
223
- - The rate is the average across everything outstanding, not the rate on
224
- anything issued today. That is why it moves slowly: a new bill reprices a
225
- fraction of the portfolio, and a bond issued in 2019 still pays its own
226
- coupon.
227
- - A page holds at most 10,000 rows and the run is around 300, so the adapter
228
- asks for the whole series in one request and does not page. A run longer
229
- than the cap would be a change worth making here rather than a silent
230
- truncation, so the row limit is a named constant.
231
- - Each row is dated to the last day of its month. A month the agency could
232
- not compute would arrive without a value, and those rows are dropped.
233
- - A request the API rejects answers with an HTTP error and a body of
234
- `{"error":"Invalid Query Param","message":"..."}`, which the parser reports
235
- as an API error rather than a shape error.
236
-
237
- ## Notes on the USGS peak-flow record
238
-
239
- - The adapter reads the `peaks` collection on the USGS Water Data OGC API
240
- (`https://api.waterdata.usgs.gov/ogcapi/v0`), not the older
241
- `waterservices.usgs.gov` endpoints. The older statistics service answers
242
- `503 Service unavailable` to roughly half of scripted requests, which is
243
- enough to fail a build; the OGC API answered every request in the same
244
- window. The daily-values service on the older host still works and is worth
245
- using when a daily series is what a story needs.
246
- - Every number in a row arrives as a string, including the peak itself, so
247
- the parser converts once and keeps the number. `value` blank means the
248
- agency filed no peak for that year, and the row is dropped rather than
249
- read as zero.
250
- - The rows are dated by **water year**, which starts on 1 October. A peak on
251
- 1955-10-08 is filed under water year 1956, so `waterYear` and
252
- `calendarYear` are separate fields and are not always the same number. The
253
- record is sorted and charted by water year, because that is the unit the
254
- agency files and the unit a flood season belongs to.
255
- - The record has holes. At the default gauge there is no row for the 17
256
- water years from 1845 to 1861, and the series lists them in
257
- `missingWaterYears` rather than drawing them as zero, because a year with
258
- no filing is not a year the river ran dry.
259
- - A gauge with several peaks in one water year is filed once, with the
260
- largest. The adapter does not de-duplicate, so a response carrying two rows
261
- for one water year would be a change in the source worth seeing.
262
- - Rows carry the agency's qualifiers, for example `UNKNOWNREGULATION` where
263
- the agency could not tell whether works upstream changed the flow, or
264
- `MAXDAILYMEAN` where the peak is a daily mean rather than an instantaneous
265
- reading. They are kept on each year rather than dropped, so the copy can be
266
- honest about them.
267
- - One keyless request covers the whole record. The default gauge answers 165
268
- water years in one page of about 126 KB.
269
-
270
- ## Notes on the CPSC recall service
271
-
272
- - The adapter reads
273
- `https://www.saferproducts.gov/RestWebServices/Recall`, the same service
274
- behind the public SaferProducts.gov search. It is keyless.
275
- - One request covers one calendar year. A range wider than a year fails: the
276
- service answers HTTP 200 with a single row whose title reads
277
- `Error retrieving Recalls: The underlying provider failed on Open.` The
278
- adapter reads a year at a time for that reason, and the parser throws on
279
- that row rather than counting it as a recall.
280
- - A year of rows is heavy as JSON. 2025 is 420 recalls in about 1.3 MB, so the
281
- default window of 2014 to the current year costs about 11 MB across 13
282
- requests. A year with no recall keeps a zero row in `years` rather than
283
- disappearing from the chart.
284
- - The newest year in the window is the year the agency is still filling, so
285
- `busiestCompleteYear` and `quietestCompleteYear` are picked from the years
286
- before it. `completeYearCount` says how many those were.
287
- - `ManufacturerCountries` names every country a recall touches, so a recall
288
- can list several and the country counts add to more than the recall count.
289
- In the 2014 to 2026 window China appears on 2,312 of 3,986 recalls, the
290
- United States on 753, and 122 countries appear at least once.
291
- - `RemedyOptions` is nearly an enum (Refund, Repair, Replace, Dispose, New
292
- Instructions, Label, Inspect) with two junk rows in it: one carries `R` and
293
- one carries a whole paragraph of consumer instructions. The adapter keeps
294
- whatever the agency sends and sorts the counts, so the tail of the list
295
- shows the mess rather than hiding it.
296
- - The service holds recalls from the 1970s. The adapter starts at 2014, which
297
- keeps the window to 13 requests and to the years a reading of the current
298
- trend needs.
299
- - The committed snapshot (`cpsc-product-recalls-2026-10-02.json`) holds the
300
- folded counts, not the raw years, because a year of raw rows is about a
301
- megabyte and the counts are a few kilobytes. The second fixture
302
- (`cpsc-recall-rows-2014-2026-sampled-2026-10-02.json`) holds the first three
303
- recalls of every year in the window, which is what the registry test answers
304
- each per-year request with.
305
-
306
- ## Notes on the Consumer Complaint Database
307
-
308
- - The adapter reads
309
- `https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/`,
310
- the search endpoint behind the public Consumer Complaint Database. It is
311
- keyless. The bureau answers a scripted request with 403 and a request with a
312
- declared user agent with 200.
313
- - The search API computes its term aggregations on every request, which takes
314
- the body from about fifteen kilobytes to about four hundred. Adding
315
- `no_aggs=true` drops them, so the adapter asks for `size=0&no_aggs=true` and
316
- reads `hits.total.value` when it only needs a count, and makes one extra
317
- request without `no_aggs` for the product and company tallies.
318
- - The API answers one question per request and has no date sort, so the window
319
- costs one request per year plus one for the tallies plus one or two to find
320
- the newest day. `findNewestCfpComplaintDate` walks back from today and stops
321
- at the first day with a complaint.
322
- - The first year in the window, 2011, holds only December: the bureau
323
- published its first complaints on 1 December 2011. It is kept rather than
324
- dropped, and the page's data note says the first bar covers one month.
325
- - The product tally splits credit reporting across more than one label because
326
- the bureau changed its product taxonomy over the years. The adapter keeps
327
- whatever the API sends rather than merging labels, so a reader of
328
- `topProducts` should not assume the labels are stable.
329
- - The committed snapshot (`cfpb-consumer-complaints-2026-10-03.json`) holds
330
- the folded counts, not the eighteen million raw rows. The registry test
331
- answers each per-year request, the newest-day probe, and the tally request
332
- from that one fixture.
333
-
334
- ## Checks
388
+ `estimatetax-income-tax`:
335
389
 
336
- ```sh
337
- npm run test --workspace @usa-open-data-connectors/usa-sources
338
- npm run test:smoke --workspace @usa-open-data-connectors/usa-sources # hits the live API
390
+ ```ts
391
+ import { fetchEstimateTaxCalculation } from '@usa-open-data-connectors/usa-sources';
392
+
393
+ const tax = await fetchEstimateTaxCalculation({
394
+ query: { income: 85_000, filingStatus: 'single', state: 'NY', city: 'new-york-city' },
395
+ });
396
+ console.log(tax.totals.totalTax, tax.totals.effectiveRate, tax.sources.federal.url);
397
+ ```
398
+
399
+ `ncbi-pubmed-search`:
400
+
401
+ ```ts
402
+ import { fetchNcbiPubmedSearch } from '@usa-open-data-connectors/usa-sources';
403
+
404
+ const result = await fetchNcbiPubmedSearch({
405
+ term: '"long COVID"[tiab] AND 2025[edat]',
406
+ retmax: 0,
407
+ });
408
+ console.log(result.totalCount, result.queryTranslation);
339
409
  ```
410
+
411
+ The public API is `NCBI_EUTILS_API_BASE`, `NCBI_PUBMED_ESEARCH_PATH`,
412
+ `NCBI_PUBMED_DEFAULT_TERM`, `NCBI_PUBMED_DEFAULT_RETMAX`, `buildNcbiPubmedSearchUrl`,
413
+ `parseNcbiPubmedSearchPayload`, `fetchNcbiPubmedSearch`, and
414
+ `ncbiPubmedSearchAdapter`, with the `NcbiPubmedSearchResult` and
415
+ `NcbiPubmedTranslation` types. The parser validates the response with a zod
416
+ schema over `header` and `esearchresult`, where `esearchresult` can carry
417
+ `count`, `retmax`, `retstart`, `idlist`, `querytranslation`, `translationset`,
418
+ `error`, and `warninglist`. The counts arrive as strings and are converted once.
419
+ An `esearchresult.error` is thrown as `UsSourceApiError` even though the HTTP
420
+ status is 200.
421
+
422
+ `clinicaltrials-studies`:
423
+
424
+ ```ts
425
+ import {
426
+ buildClinicalTrialsStudiesUrl,
427
+ parseClinicalTrialsStudiesPayload,
428
+ } from '@usa-open-data-connectors/usa-sources';
429
+
430
+ const url = new URL(buildClinicalTrialsStudiesUrl({ pageSize: 1000 }));
431
+ url.searchParams.set('query.cond', 'long COVID');
432
+ url.searchParams.set(
433
+ 'fields',
434
+ 'NCTId,BriefTitle,OverallStatus,StartDate,StudyType,Phase,Condition,LeadSponsorName,EnrollmentCount,EnrollmentType,LastUpdatePostDate'
435
+ );
436
+ const response = await fetch(url);
437
+ if (!response.ok) {
438
+ throw new Error(`HTTP ${response.status} from ${url.toString()}`);
439
+ }
440
+ const page = parseClinicalTrialsStudiesPayload(await response.json());
441
+ console.log(page.studyCount, page.statusCounts[0], page.nextPageToken);
442
+ ```
443
+
444
+ The public API is `CLINICALTRIALS_STUDIES_API_BASE`,
445
+ `CLINICALTRIALS_STUDIES_PAGE_SIZE`, `CLINICALTRIALS_STUDIES_SOURCE_ID`,
446
+ `buildClinicalTrialsStudiesUrl`, `parseClinicalTrialsStudiesPayload`,
447
+ `fetchClinicalTrialsStudies`, and `clinicalTrialsStudiesAdapter`, with the
448
+ `ClinicalTrialStudy`, `ClinicalTrialStatusCount`, and `ClinicalTrialStudyPage`
449
+ types. The parser validates the response with a zod schema over `studies` and
450
+ `nextPageToken`. Each study carries an optional `protocolSection` with
451
+ `identificationModule` (`nctId`, `briefTitle`), `statusModule`
452
+ (`overallStatus`, `startDateStruct`, `lastUpdatePostDateStruct`),
453
+ `sponsorCollaboratorsModule` (`leadSponsor.name`), `conditionsModule`
454
+ (`conditions`), and `designModule` (`studyType`, `phases`, `enrollmentInfo`). A
455
+ study without a registry id or a brief title throws `UsSourceParseError`, and an
456
+ empty page also throws.
457
+
458
+ The adapter reads one page of the register and keeps the id, title, status,
459
+ study type, phases, conditions, lead sponsor, enrollment, start date, and last
460
+ update date. The default page size is ten. `buildClinicalTrialsStudiesUrl` takes
461
+ only `pageSize`, so a filtered story builds the URL and adds `query.cond`,
462
+ `query.term`, or `filter.*` itself before calling the parser. The full protocol
463
+ record is large: 737 long COVID studies were 12.2 MB without a field projection
464
+ and 426 KB with the eleven fields above on 2026-10-20. The `fields` parameter
465
+ accepts those short field names, so a caller does not need to ask for the whole
466
+ record.
467
+
468
+ Three shape notes. A start date can be day precision (`2024-12-10`) or month
469
+ precision (`2026-05`), and the adapter keeps the string as the registry wrote
470
+ it. A start date can be planned rather than actual, so a recent year can still
471
+ gain studies. `query.cond` is the registry's own condition search rather than a
472
+ filter over the `conditions` array; a long COVID search returned 737 studies on
473
+ 2026-10-20 while only 315 of them carried a condition string containing "long
474
+ COVID", because keywords and related terms also match. The response has no
475
+ total count unless the request adds `countTotal=true`, so count `studies` for
476
+ the page or ask for the total separately.
477
+
478
+ `nws-point-forecast`:
479
+
480
+ ```ts
481
+ import { fetchNwsPointForecast } from '@usa-open-data-connectors/usa-sources';
482
+
483
+ const point = await fetchNwsPointForecast({ latitude: 47.601, longitude: -122.33 });
484
+ console.log(point.gridId, point.gridX, point.gridY, point.forecastUrl);
485
+ ```
486
+
487
+ The public API is `NWS_API_BASE`, `NWS_POINT_FORECAST_PATH`,
488
+ `NWS_POINT_FORECAST_SOURCE_ID`, `NWS_USER_AGENT`, `NWS_DEFAULT_LATITUDE`,
489
+ `NWS_DEFAULT_LONGITUDE`, `buildNwsPointUrl`, `parseNwsPointForecastPayload`,
490
+ `fetchNwsPointForecast`, and `nwsPointForecastAdapter`, with the
491
+ `NwsForecastPoint` type. The parser validates the response with a zod schema
492
+ over `type`, `geometry.coordinates`, and a `properties` object carrying
493
+ `city`, `state`, `gridId`, `gridX`, `gridY`, `forecastOffice`, `forecast`,
494
+ `forecastHourly`, `forecastGridData`, `observationStations`, `timeZone`,
495
+ `radarStation`, `county`, `fireWeatherZone`, and the nested
496
+ `relativeLocation.properties` place label. A response that is not a GeoJSON
497
+ feature, has no coordinate pair, or has no grid id, grid cell, forecast URL, or
498
+ forecast office throws `UsSourceParseError`.
499
+
500
+ Three shape notes. The point lookup returns the forecast grid and the URLs that
501
+ lead to the forecast, not the forecast periods themselves; a caller that needs
502
+ temperatures reads `forecastUrl` or `forecastHourlyUrl` next. Grid cell numbers
503
+ are local to an office, so `gridX` 100 in one office and `gridX` 100 in another
504
+ point at different places; always keep `gridId` next to the cell. The place
505
+ label is the nearest place the service knows, not the caller's own name: a
506
+ lookup for Tacoma came back as "Seattle" on 2026-10-20, and a lookup for
507
+ Tacoma Old City Hall landed in office SEW at cell 125,68.
508
+
509
+ `callook-callsigns`:
510
+
511
+ ```ts
512
+ import { fetchCallookCallsigns } from '@usa-open-data-connectors/usa-sources';
513
+
514
+ const directory = await fetchCallookCallsigns({ callsigns: ['W1AW'] });
515
+ console.log(directory.callsigns[0]?.callsign, directory.soonestExpiry?.expiryDate);
516
+ ```
517
+
518
+ Callook answers one callsign per request. The default fetch reads W1ABC through
519
+ W0ABC, one callsign for each US call district. Pass `callsigns` to read a
520
+ different set. The parser keeps a lookup with status `INVALID` or `UPDATING` as
521
+ a record with no licence fields, and counts only `VALID` records in the folded
522
+ totals.
523
+
524
+ `college-scorecard-institutions`:
525
+
526
+ ```ts
527
+ import { fetchCollegeScorecardInstitutions } from '@usa-open-data-connectors/usa-sources';
528
+
529
+ const snapshot = await fetchCollegeScorecardInstitutions();
530
+ console.log(snapshot.institutionCount, snapshot.highestEarningState?.stateAbbr);
531
+ ```
532
+
533
+ The adapter reads the keyless institution-level bulk download, not the keyed
534
+ College Scorecard API. The archive is a zip that holds one wide CSV, and a live
535
+ fetch takes about 20 seconds on a fast connection.
536
+
537
+ ## Notes and limits
538
+
539
+ - `bls-unemployment-rate`: the public API refuses a window longer than ten years. `fetchBlsSeries` splits a longer range into windows, so two decades cost three calls once the range reaches the current year.
540
+ - `bls-unemployment-rate`: keyless access allows 25 series queries a day. A registered key allows 500, and the adapter takes `apiKey` for the day that matters.
541
+ - `bls-unemployment-rate`: the agency uses `-` for a month it could not publish and `M13` for an annual average. The parser drops both, so one year holds twelve monthly values.
542
+ - `usgs-hawaii-earthquakes`: the event query answers a window of any length, one request per window. The caller sets the box, the magnitude floor, and the dates.
543
+ - `usgs-hawaii-earthquakes`: the feed mixes tectonic earthquakes with quarry blasts, explosions, and ice quakes. A row can arrive without a magnitude. The parser keeps only tectonic earthquakes that carry a magnitude and a depth.
544
+ - `usgs-hawaii-earthquakes`: the catalogue answers newest first. `parseUsgsEarthquakes` returns oldest first, so a caller reading index 0 gets the start of the window.
545
+ - `cdc-county-obesity`: the resource id is per release. This adapter reads the 2025 release. Its estimates come from the 2023 Behavioral Risk Factor Surveillance System (BRFSS) and the Census Bureau 2023 county population estimates. A new release gets a new id, so a caller pinned to this one keeps reading the numbers it was written against.
546
+ - `cdc-county-obesity`: every row is a model-based estimate, not a count. The release publishes each measure as crude prevalence and as age-adjusted prevalence. The adapter reads the crude rows, because age-adjusted rows answer a different question.
547
+ - `cdc-county-obesity`: a county the model could not estimate arrives with no value. The release carries no obesity rows for Kentucky and Pennsylvania. The parser drops rows without a value and keeps the national row apart from the counties.
548
+ - `ncei-annual-temperature`: the download is a CSV, not JSON. It holds two comment lines, a header, then one row per year as `YYYYMM,value` in degrees Fahrenheit. `parseNceiAnnualTemperatureCsv` skips the comments and the header, and stops on any other line shape rather than dropping it.
549
+ - `ncei-annual-temperature`: the URL carries a window length and a window end month. Twelve months ending in December is the calendar year. The parser refuses a file with more than one row per year.
550
+ - `ncei-annual-temperature`: the current year joins the file only after December closes it, so the newest row is the last complete calendar year. The 2026 download is dated 2026-09-26 and ends at 2025.
551
+ - `noaa-sea-level`: one request covers the whole window. The `monthly_mean` product answers one row per month rather than one row per reading, so 170 years cost one call and about 460 KB. The endpoint is keyless.
552
+ - `noaa-sea-level`: values are metres against the station Mean Sea Level (MSL) datum, the average of hourly heights over the 1983-2001 National Tidal Datum Epoch. Reading the whole record against one fixed epoch is what makes two years comparable.
553
+ - `noaa-sea-level`: the API answers a row for every month in the window and leaves the value blank when it has none. Blank rows are dropped. A year's average is taken over the months it has, so `monthCount` says how much of the year a value covers.
554
+ - `noaa-sea-level`: errors come back in the body with HTTP 200, as `{"error":{"message":"..."}}`. The parser checks for that before reading the data. A wrong product name answers with plain text instead, which fails the shape check.
555
+ - `treasury-avg-interest-rate`: one keyless request covers the whole run. The dataset answers a row per month per security type. The filter pins the response to `security_type_desc:eq:Interest-bearing Debt`. That is the portfolio total the bureau's monthly statement is built from. The other rows never reach the parser. They hold the rates on bills, notes, bonds, and non-marketable debt.
556
+ - `treasury-avg-interest-rate`: the rate is the average across everything outstanding, not the rate on anything issued today. That is why it moves slowly. A new bill reprices a fraction of the portfolio, and a bond issued in 2019 still pays its own coupon.
557
+ - `treasury-avg-interest-rate`: a page holds at most 10,000 rows and the run is around 300. The adapter asks for the whole series in one request and does not page. The row limit is a named constant.
558
+ - `treasury-avg-interest-rate`: each row is dated to the last day of its month. A month the agency could not compute arrives without a value, and the parser drops those rows.
559
+ - `treasury-avg-interest-rate`: a request the API rejects answers with an HTTP error and a body of `{"error":"Invalid Query Param","message":"..."}`. The parser reports an API error rather than a shape error.
560
+ - `treasury-debt-to-penny`: one keyless request covers the whole run if the row limit is raised. The file starts on 1993-04-01 and held 8,415 business-day rows on 2026-10-16, about 2.8 MB. The default `TREASURY_DEBT_TO_PENNY_ROW_LIMIT` is 10 and the API returns oldest first, so the default page is the first ten days of 1993 rather than recent debt. Pass `{ rowLimit: 10_000 }` to read the whole series in one request.
561
+ - `treasury-debt-to-penny`: the API caps `page[size]` at 10,000 rows and the run still fits under that cap. A caller asking for more gets the cap's worth, so check `dayCount` against the API's `meta.count` when the run grows past it.
562
+ - `treasury-debt-to-penny`: every value arrives as a string, and a figure the agency could not report arrives as the literal text `"null"`. The adapter reads that as null and drops a row with no total. The first row, 1993-04-01, has a total but no public or intragovernmental split.
563
+ - `treasury-debt-to-penny`: `tot_pub_debt_out_amt` equals `debt_held_public_amt` plus `intragov_hold_amt` on the rows that carry all three. Keep the split fields when the story compares public debt with intragovernmental holdings, and use the total field for the headline.
564
+ - `treasury-debt-to-penny`: rows cover business days only. A weekend or federal holiday has no row, so a daily series has gaps by design. Use the `record_date` rather than an index when aligning debt with another daily series.
565
+ - `treasury-debt-to-penny`: `sort=-record_date` works on the API but the URL builder does not set it. The helper reads the oldest-first default, which keeps a fixture and a live response in the same order. A rejected request answers with an HTTP error and a body of `{"error":"...","message":"..."}`.
566
+ - `usgs-peak-streamflow`: the adapter reads the `peaks` collection on the USGS Water Data OGC API. It does not read the older `waterservices.usgs.gov` endpoints. The older statistics service answers `503 Service unavailable` to about half of scripted requests, which is enough to fail a build. The daily-values service on the older host still works when a daily series is what a story needs.
567
+ - `usgs-peak-streamflow`: every number in a row arrives as a string, including the peak itself. The parser converts once and keeps the number. A blank `value` means the agency filed no peak for that year. The parser drops that row.
568
+ - `usgs-peak-streamflow`: the rows are dated by water year, which starts on 1 October. A peak on 1955-10-08 is filed under water year 1956, so `waterYear` and `calendarYear` are separate fields and are not always the same number.
569
+ - `usgs-peak-streamflow`: the record has holes. At the default gauge there is no row for the 17 water years from 1845 to 1861. The series lists them in `missingWaterYears` rather than drawing them as zero.
570
+ - `usgs-peak-streamflow`: a gauge with several peaks in one water year is filed once, with the largest. The adapter does not de-duplicate, so two rows for one water year would be a change in the source worth seeing.
571
+ - `usgs-peak-streamflow`: rows carry the agency's qualifiers, for example `UNKNOWNREGULATION` or `MAXDAILYMEAN`. The parser keeps them on each year.
572
+ - `usgs-peak-streamflow`: one keyless request covers the whole record. The default gauge answers 165 water years in one page of about 126 KB.
573
+ - `cpsc-product-recalls`: the adapter reads the same service behind the public SaferProducts.gov search. It is keyless.
574
+ - `cpsc-product-recalls`: one request covers one calendar year, and a wider range fails. The service answers HTTP 200 with a single row. Its title reads `Error retrieving Recalls: The underlying provider failed on Open.` The adapter reads a year at a time for that reason. The parser throws on that row rather than counting it as a recall.
575
+ - `cpsc-product-recalls`: a year of rows is heavy as JSON. 2025 is 420 recalls in about 1.3 MB, so the default window of 2014 to the current year costs about 11 MB across 13 requests. A year with no recall keeps a zero row in `years`.
576
+ - `cpsc-product-recalls`: the newest year is the year the agency is still filling, so `busiestCompleteYear` and `quietestCompleteYear` are picked from the years before it. `completeYearCount` says how many those were.
577
+ - `cpsc-product-recalls`: `ManufacturerCountries` names every country a recall touches, so a recall can list several countries. The country counts add to more than the recall count. In the 2014 to 2026 window China appears on 2,312 of 3,986 recalls. The United States appears on 753, and 122 countries appear at least once.
578
+ - `cpsc-product-recalls`: `RemedyOptions` is nearly an enum (Refund, Repair, Replace, Dispose, New Instructions, Label, Inspect) with two junk rows. One carries `R` and one carries a whole paragraph of consumer instructions. The adapter keeps whatever the agency sends and sorts the counts.
579
+ - `cpsc-product-recalls`: the service holds recalls from the 1970s. The adapter starts at 2014, which keeps the window to 13 requests.
580
+ - `cfpb-consumer-complaints`: the adapter reads the search endpoint behind the public Consumer Complaint Database. It is keyless. The bureau answers a scripted request with 403 and a request with a declared user agent with 200.
581
+ - `cfpb-consumer-complaints`: the search API computes its term aggregations on every request, which takes the body from about fifteen kilobytes to about four hundred. The adapter asks for `size=0&no_aggs=true` and reads `hits.total.value` when it only needs a count. It makes one extra request without `no_aggs` for the product and company tallies.
582
+ - `cfpb-consumer-complaints`: the API answers one question per request and has no date sort. The window costs one request per year plus one for the tallies plus one or two to find the newest day. `findNewestCfpComplaintDate` walks back from today. It stops at the first day with a complaint.
583
+ - `cfpb-consumer-complaints`: the first year in the window, 2011, holds only December. The bureau published its first complaints on 1 December 2011. The adapter keeps the year, and the page's data note says the first bar covers one month.
584
+ - `cfpb-consumer-complaints`: the product tally splits credit reporting across more than one label because the bureau changed its product taxonomy over the years. The adapter keeps whatever the API sends, so the labels in `topProducts` are not stable.
585
+ - `cfpb-consumer-complaints`: the committed snapshot holds the folded counts, not the eighteen million raw rows.
586
+ - `fdic-bank-directory`: the default request asks for ten rows and sets no active filter. The full file held 27,835 records on 2026-10-16, including historical institutions the FDIC marks inactive, so the first page is not a current bank count. Add `filters=ACTIVE:1` to the URL to read the active directory, which held 4,219 institutions that day.
587
+ - `fdic-bank-directory`: the filtered count arrives in both `meta.total` and `totals.count`. Both fields describe the filtered result, not the size of the unfiltered file, so `totalBanks` is the active count when the request carries the active filter.
588
+ - `fdic-bank-directory`: `banks.data.fdic.gov` answers with a redirect to `api.fdic.gov`. `fetch` follows it. Both hosts answer 200 to a scripted request, and neither needs a key.
589
+ - `fdic-bank-directory`: `FDIC_BANK_FIELDS` asks for `NAME,CITY,STALP`, but the parser requires `ID` as well. The service adds `ID` to the response even when it is not in `fields`, but a caller should ask for `ID,NAME,CITY,STALP` so the request and the parser agree without relying on that behaviour.
590
+ - `fdic-bank-directory`: each row sits inside a `data` wrapper with a separate `score` field. The parser reads `row.data`, not the wrapper.
591
+ - `fdic-bank-directory`: the active rows cover 55 state-level codes, not 50. DC, Puerto Rico, Guam, Micronesia, and the US Virgin Islands each hold active banks, so a caller that labels the counts as states will overstate the map.
592
+ - `fdic-bank-directory`: the directory counts institutions at the state of the main office. It is not a branch count, and a bank with branches across several states appears once.
593
+ - `sec-edgar-filings`: the SEC asks every caller to identify itself with a User-Agent header. The adapter sends `SEC_EDGAR_USER_AGENT`, which carries a placeholder contact address. A deployment should override it with its own application name and address before the request leaves a shared host.
594
+ - `sec-edgar-filings`: the submissions file carries the last 1,000 filings, not the issuer's full history. Apple's block ran from 18 September 2015 to 16 October 2026 on 2026-10-19. A caller that needs older rows has to read the filing archive rather than this endpoint.
595
+ - `sec-edgar-filings`: the default CIK is Apple's, `0000320193`. `fetchSecEdgarFilings({ cik })` accepts one to ten digits and zero-pads the value, so `320193` and `0000320193` ask for the same issuer.
596
+ - `sec-edgar-filings`: the `recent` block is a set of parallel arrays, not one object per filing. A column the form does not carry arrives as an empty string. The parser turns a blank report date or document description into null, and stops rather than guessing when a column length differs from the accession list.
597
+ - `sec-edgar-filings`: a filing's `size` is in bytes and describes the submission file. It is not the size of the main document, and it is not a count of pages.
598
+ - `epa-envirofacts-facilities`: the state filter takes the value directly after the column name, as `TRI_FACILITY/STATE_ABBR/CA/rows/0:9999/JSON`. Adding an `equals` segment is accepted but ignored, and the service then returns the first rows of the whole table instead of the state asked for.
599
+ - `epa-envirofacts-facilities`: the default row window is the first ten rows. `fetchEpaEnvirofactsFacilities` takes a state but not a row window, so a caller that needs the whole state builds the URL with `buildEpaEnvirofactsFacilitiesUrl({ stateAbbr, rowWindow })` and parses the response with `parseEpaEnvirofactsFacilitiesPayload`.
600
+ - `epa-envirofacts-facilities`: the table holds facility records, not release amounts. It is the Toxics Release Inventory facility register, so a row says where a site is and whether the EPA marks it closed (`fac_closed_ind` of `1`), not how much it released in a year.
601
+ - `epa-envirofacts-facilities`: every key arrives in lower case, and the parent company is sometimes the literal text `NA`. The parser reads that as null rather than a company name.
602
+ - `epa-envirofacts-facilities`: a state response can be large. California held 5,110 facilities on 2026-10-19, about 7.7 MB with the full record and about 1 MB after trimming to the ten fields the parser keeps.
603
+ - `estimatetax-income-tax`: one keyless request answers the federal, state, and local income tax for one salary. The endpoint is limited to 60 requests a minute per address.
604
+ - `estimatetax-income-tax`: the response is a calculation from the publisher's own engine, not a row from an official statistical release. Every layer cites the IRS or state document it was read from, so the source and the date are visible in the response.
605
+ - `estimatetax-income-tax`: the engine assumes the standard deduction unless `preTaxDeductions` is supplied. It does not model itemised deductions, capital gains, or credits, and the response carries that disclaimer.
606
+ - `estimatetax-income-tax`: the default query is the $85,000 single filer in New York City recorded in the fixture. A federal-only query sets `state` to null.
607
+ - `ncbi-pubmed-search`: the default term is `asthma` and the default page is ten ids. Pass `term` for another query and `retmax: 0` when only the match count is needed; the count still arrives.
608
+ - `ncbi-pubmed-search`: `[dp]` means Date - Publication, and PubMed can hold both an electronic and a print date for one record. Year buckets built with `[dp]` can overlap, so their counts can add to more than the unfiltered total. Use `[edat]` (Date - Entry) when each record must fall in exactly one year.
609
+ - `ncbi-pubmed-search`: keyless NCBI allows about three requests a second. The adapter makes one request per call and does not add a delay, so a caller reading six years must space the calls itself.
610
+ - `ncbi-pubmed-search`: a query field tag such as `[tiab]` restricts the match to the title and abstract. Without a tag, PubMed maps the words through MeSH and searches every indexed field, which answers a wider and different count. `queryTranslation` shows what PubMed actually ran.
611
+ - `ncbi-pubmed-search`: a bad query answers HTTP 200 with `esearchresult.error` instead of a result block. The parser turns that into `UsSourceApiError`, so a caller does not count an error as zero papers.
612
+ - `cdc-socrata-catalogue`: the default request asks for ten rows. The discovery API answers the whole catalogue when the limit is high enough, and the CDC catalogue held 1,478 entries on 2026-10-20. Call `buildCdcSocrataCatalogueUrl({ limit: 10_000 })` or pass `limit` to `fetchCdcSocrataCatalogue` when you need every entry. A limit below the catalogue size truncates the response without an error.
613
+ - `cdc-socrata-catalogue`: each row is a catalogue entry, not a dataset record. `assetType` can be `dataset`, `file`, `href`, `filter`, `chart`, `story`, or `map`. A category can hold one type or a mix, so count the asset type before you describe a category as a dataset collection.
614
+ - `cdc-socrata-catalogue`: an entry without a category has `null` in the response. The adapter keeps the null on the entry and counts it under `Uncategorised` in `categories`. That bucket is an adapter label, not a category the CDC assigned.
615
+ - `cdc-socrata-catalogue`: the full response is large because each row carries portal metadata. The 1,478-row response was about 7.9 MB on 2026-10-20. The parser keeps nine fields, and a fixture trimmed to those fields was about 491 KB.
616
+ - `cdc-socrata-catalogue`: the discovery API can carry rights and metadata fields, but this parser does not keep them. Check the portal entry before you reuse an item.
617
+ - `bts-transportation-stats`: the default request asks for ten rows. The story that consumed this adapter needed the whole catalogue, so it calls `buildBtsTransportationStatsUrl({ limit: 10_000 })` and parses the full response with `parseBtsTransportationStatsPayload`. A lower limit truncates the catalogue without an error.
618
+ - `bts-transportation-stats`: each row is a catalogue entry, not a dataset record. `assetType` can be `story`, `dataset`, `chart`, `measure`, `filter`, `href`, `map`, or `file`. The story and dataset counts are different questions, and the 2026-10-20 catalogue held 177 stories against 153 datasets.
619
+ - `bts-transportation-stats`: `viewCount` and `downloadCount` are the portal's own counters for an item page and its files. They are not unique people. Stories carry views and a zero download count because Socrata does not give a story a download counter, while 268 entries reported no downloads at all on 2026-10-20.
620
+ - `bts-transportation-stats`: the response is large because every entry carries portal metadata. The 589-row response was about 2.2 MB on 2026-10-20. The parser keeps nine fields, and a fixture trimmed to those fields was about 172 KB.
621
+ - `bts-transportation-stats`: an entry without a category has `null` in the response. The adapter keeps the null on the entry and counts it under `Uncategorised` in `categories`. That bucket is an adapter label, not a category the bureau assigned.
622
+ - `nyc-open-data-catalogue`: the discovery API answers a bare JSON array of catalogue entries, not dataset rows. An entry can arrive without a category, which the adapter counts under `Uncategorised`.
623
+ - `nyc-open-data-catalogue`: the page captured on 2026-10-19 mixes datasets and maps. The adapter counts both, and keeps the view and download totals for the page it read.
624
+ - Unit tests use the committed fixtures and run offline. Run them with `npm run test --workspace @usa-open-data-connectors/usa-sources`.
625
+ - Smoke tests hit the live APIs and need `RUN_SMOKE=1`. Run them with `npm run test:smoke --workspace @usa-open-data-connectors/usa-sources`.
626
+
627
+ - `nws-point-forecast`: one keyless request covers one coordinate. The service asks every caller to send a User-Agent that identifies the application, and `fetchNwsPointForecast` sends `NWS_USER_AGENT` on every call.
628
+ - `nws-point-forecast`: the response is a GeoJSON feature whose `properties` hold the grid and the links. The forecast itself is a second request to `forecastUrl` (daily periods) or `forecastHourlyUrl` (hourly periods).
629
+ - `nws-point-forecast`: `gridX` and `gridY` are numbered inside one forecast office. The same pair in two offices is not the same place, and there is no national grid coordinate in the response.
630
+ - `nws-point-forecast`: `relativeLocation.properties` names the nearest place the service knows, which can be a neighboring city. A Tacoma lookup returned "Seattle", so a caller that needs its own label should keep it outside the response.
631
+ - `nws-point-forecast`: one point response is about 3.5 KB. Twenty-five lookups cost twenty-five requests and about 88 KB of committed JSON when saved as a snapshot.
632
+ - `callook-callsigns`: the service answers one callsign per request and has no bulk list endpoint. The default ten-district sample costs ten requests. The service asks for no key, and its API reference says the data comes from the FCC licence file, refreshed daily.
633
+ - `callook-callsigns`: a lookup returns `INVALID` for a callsign with no current licence. The adapter keeps that answer as a record with status `INVALID` rather than treating it as an HTTP failure. A callsign with a current licence has status `VALID` and carries the licence fields.
634
+ - `callook-callsigns`: coordinates and the grid square are calculated by Callook from the mailing address, not taken from the FCC file. A record can have an empty location. The adapter keeps the coordinates as null in that case and does not invent a place.
635
+ - `college-scorecard-institutions`: the bulk archive is 24 MB and the CSV inside it is 102 MB. The adapter raises its request timeout to two minutes. The CSV has 3,379 columns, so the adapter reads only the outcome fields it exposes.
636
+ - `college-scorecard-institutions`: `NA` and `PS` mean the Department of Education did not publish a figure. The adapter keeps those as null, so a chart does not turn a suppressed value into a zero.
637
+ - `college-scorecard-institutions`: the state summary is a median of the institution medians, not a weighted student average. It answers "what does a typical college in this state report", not "what does a typical student earn".
638
+ - `college-scorecard-institutions`: the earnings field is `MD_EARN_WNE_P10`, the median for graduates working and not enrolled ten years after entry. It is not a first-year salary.
639
+ - `nextbus-transit-vehicles`: the adapter reads one agency route, the Portland Streetcar NS line (`portland-sc`, route `193`). The feed also serves other agencies, including non-US ones, so the adapter does not claim to cover every agency on the feed.
640
+ - `nextbus-transit-vehicles`: `t=0` asks for every report the feed still holds for the route. The feed returns the current set of streetcars, not a history, so the snapshot describes one moment and is replaced on the next request.
641
+ - `nextbus-transit-vehicles`: a route with no service returns a snapshot with zero vehicles and a valid timestamp. That is a real answer, not a failure, so the parser keeps it.
642
+ - `nextbus-transit-vehicles`: the feed carries a copyright notice for the transit agency and Umo IQ, and states no open licence. Check the agency's terms before republishing the positions.
643
+
644
+ ## Data sources and licences
645
+
646
+ The data comes from US federal agencies and the open data catalogue services they publish on.
647
+
648
+ | Publisher | Source URL |
649
+ | --- | --- |
650
+ | Bureau of Labor Statistics | `https://api.bls.gov/publicAPI/v2/timeseries/data` |
651
+ | US Geological Survey | `https://earthquake.usgs.gov/fdsnws/event/1/query` and `https://api.waterdata.usgs.gov/ogcapi/v0/collections/peak/items` |
652
+ | Centers for Disease Control and Prevention | `https://data.cdc.gov/resource/swc5-untb.json` and `https://data.cdc.gov/api/views.json` |
653
+ | NOAA National Centers for Environmental Information | `https://www.ncei.noaa.gov/access/monitoring/climate-at-a-glance` |
654
+ | NOAA Center for Operational Oceanographic Products and Services | `https://api.tidesandcurrents.noaa.gov/api/prod/datagetter` |
655
+ | US Department of the Treasury | `https://api.fiscaldata.treasury.gov/services/api/fiscal_service/v2/accounting/od/avg_interest_rates` and `https://api.fiscaldata.treasury.gov/services/api/fiscal_service/v2/accounting/od/debt_to_penny` |
656
+ | Federal Emergency Management Agency | `https://www.fema.gov/api/open/v1/FemaWebDisasterDeclarations` |
657
+ | US Food and Drug Administration | `https://api.fda.gov/food/enforcement.json` |
658
+ | US Consumer Product Safety Commission | `https://www.saferproducts.gov/RestWebServices/Recall` |
659
+ | Consumer Financial Protection Bureau | `https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/` |
660
+ | US Securities and Exchange Commission | `https://data.sec.gov/submissions/CIK0000320193.json` |
661
+ | Federal Deposit Insurance Corporation | `https://banks.data.fdic.gov/api/institutions` |
662
+ | ClinicalTrials.gov | `https://clinicaltrials.gov/api/v2/studies` |
663
+ | National Weather Service | `https://api.weather.gov/points/38.8894,-77.0352` |
664
+ | USAspending | `https://api.usaspending.gov/api/v2/references/toptier_agencies/` |
665
+ | Environmental Protection Agency | `https://data.epa.gov/efservice/TRI_FACILITY/STATE_ABBR/RI/rows/0:9/JSON` |
666
+ | National Center for Biotechnology Information | `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi` |
667
+ | Bureau of Transportation Statistics | `https://data.bts.gov/api/views.json` |
668
+ | EstimateTax | `https://estimatetax.net/api/v1/calculate` |
669
+ | HealthData.gov | `https://healthdata.gov/api/views.json` |
670
+ | New York City Open Data | `https://data.cityofnewyork.us/api/views.json` |
671
+ | Callook.info | `https://callook.info/[callsign]/json` |
672
+ | College Scorecard, US Department of Education | `https://ed-public-download.scorecard.network/downloads/Most-Recent-Cohorts-Institution_10152026.zip` |
673
+
674
+ Each publisher sets its own terms for its data. The Socrata discovery responses can carry rights and
675
+ metadata fields, but the catalogue adapters in this package keep a subset of the entry fields and do not
676
+ retain those terms. Check the licence on the portal entry before you reuse an item. A catalogue can list
677
+ entries under terms such as these:
678
+
679
+ - USA.gov public-domain label: `http://www.usa.gov/publicdomain/label/1.0/`
680
+ - USA.gov government works: `https://www.usa.gov/government-works`
681
+ - Open Database License (ODbL): `http://opendefinition.org/licenses/odc-odbl/`
682
+ - Public Domain Dedication and License (PDDL): `http://opendatacommons.org/licenses/pddl/1.0/`
683
+
684
+ Check the licence field on each entry before you reuse it. This section is not legal advice. The package
685
+ licence covers the code, not the data.
686
+
687
+ EstimateTax is a private publisher rather than a federal agency. Its API terms ask for attribution
688
+ and the source document alongside any figure you display, and the calculation response cites the
689
+ official document behind each tax figure. No open licence is stated for the API output, so credit
690
+ EstimateTax and the cited authority when you republish a calculation.
691
+
692
+ ## Package licence
693
+
694
+ MIT. See LICENSE.
695
+
696
+ ## Links
697
+
698
+ - npm: https://www.npmjs.com/package/@usa-open-data-connectors/usa-sources
699
+ - source: https://github.com/olitreadwell/usa-open-data-connectors/tree/main/packages/usa-sources
700
+ - docs: ../../docs/CONNECTOR_DISCOVERY.md