← back to CelebritySignatures
Repoint TM register fetcher to USPTO Open Data Portal (trtdxfap); document WAF + ID.me-key reality, add --file/--odp/--url ingest modes
9a722d34a5b4734a6ab2c38edc398e02259824cd · 2026-08-06 09:34:58 -0700 · Steve
Legacy bulkdata.uspto.gov is retired; ODP is WAF-gated + BSD API needs an ID.me
key, so headless fetch is blocked. Fetcher now supports ingesting a browser/
openclaw-downloaded file (--file, $0 no key), the keyed BSD API (--odp), or a
direct URL. Store stays empty (only-legally-clear) until a real file is ingested.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M scripts/fetch-tm-register.mjs
Diff
commit 9a722d34a5b4734a6ab2c38edc398e02259824cd
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Aug 6 09:34:58 2026 -0700
Repoint TM register fetcher to USPTO Open Data Portal (trtdxfap); document WAF + ID.me-key reality, add --file/--odp/--url ingest modes
Legacy bulkdata.uspto.gov is retired; ODP is WAF-gated + BSD API needs an ID.me
key, so headless fetch is blocked. Fetcher now supports ingesting a browser/
openclaw-downloaded file (--file, $0 no key), the keyed BSD API (--odp), or a
direct URL. Store stays empty (only-legally-clear) until a real file is ingested.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
scripts/fetch-tm-register.mjs | 96 ++++++++++++++++++++++++++++++-------------
1 file changed, 68 insertions(+), 28 deletions(-)
diff --git a/scripts/fetch-tm-register.mjs b/scripts/fetch-tm-register.mjs
index 6a86dfb..b6f232f 100644
--- a/scripts/fetch-tm-register.mjs
+++ b/scripts/fetch-tm-register.mjs
@@ -6,52 +6,92 @@
// ID.me-linked key and look up by serial#, not by mark name. The free, $0, no-rate-
// limit path is the published bulk register, which we index locally once.
//
-// SOURCE: USPTO Trademark Daily XML — https://bulkdata.uspto.gov/data/trademark/dailyxml/applications/
-// Each daily zip (apcYYMMDD.zip) holds <case-file> records with the mark text,
-// status, and Nice classification. The annual full-register file is the complete
-// set (tens of GB) — pull it once for a true full screen.
+// SOURCE (2026): the legacy host bulkdata.uspto.gov is RETIRED. Trademark bulk
+// data now lives on the USPTO Open Data Portal:
+// dataset page : https://data.uspto.gov/bulkdata/datasets/trtdxfap
+// product : "Trademark Daily XML File (TDXF) applications" (trtdxfap)
+// Each file is XML with <case-file> records (mark text, status, Nice class).
//
-// This script does NOT auto-download gigabytes. Usage:
-// node scripts/fetch-tm-register.mjs --url <zip-or-xml-url> # fetch ONE file
-// node scripts/fetch-tm-register.mjs --list # print the daily-xml index URL + guidance
-// Files land in tmp_tm_cache/ ; then run build-tm-index.mjs.
-import { mkdir, writeFile } from 'node:fs/promises';
+// REALITY (verified 2026-08-06): the ODP is behind an AWS WAF anti-bot challenge
+// AND its Bulk-Search/Download (BSD) API requires an ID.me-linked USPTO API key
+// (X-API-KEY). So a plain headless fetch is BLOCKED. Three real ways in:
+// (A) --file <path> : ingest a file YOU downloaded in a real browser (or via
+// the openclaw/portal-driver real-Chrome flow). $0, no key.
+// (B) --odp <fileName>: hit the BSD download API with USPTO_ODP_API_KEY (env/.env)
+// — needs Steve's ID.me-linked key. $0 once keyed.
+// (C) --url <httpsUrl>: fetch a direct file URL (if you have an unwalled one).
+// Files land in tmp_tm_cache/ ; then run build-tm-index.mjs + build-tm-clearance.mjs.
+import { mkdir, writeFile, copyFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
-import { join } from 'node:path';
+import { join, basename } from 'node:path';
+import { readFileSync } from 'node:fs';
const ROOT = fileURLToPath(new URL('..', import.meta.url));
const CACHE = join(ROOT, 'tmp_tm_cache');
-const DAILY = 'https://bulkdata.uspto.gov/data/trademark/dailyxml/applications/';
+const DATASET = 'https://data.uspto.gov/bulkdata/datasets/trtdxfap';
+const BSD_DOWNLOAD = 'https://data.uspto.gov/api/v1/datasets/products/files'; // BSD download base (key-gated)
const args = process.argv.slice(2);
const get = (flag) => { const i = args.indexOf(flag); return i >= 0 ? args[i + 1] : null; };
+function envVal(name) {
+ if (process.env[name]) return process.env[name];
+ try { const m = readFileSync(join(ROOT, '.env'), 'utf8').match(new RegExp('^' + name + '=(.+)$', 'm')); if (m) return m[1].trim(); } catch {}
+ return null;
+}
async function main() {
await mkdir(CACHE, { recursive: true });
if (args.includes('--list') || args.length === 0) {
- console.log('USPTO bulk trademark register (free, $0):');
- console.log(' Daily XML index : ' + DAILY);
- console.log(' Full register : the annual application file on that server (large).');
- console.log('');
- console.log('Fetch one file: node scripts/fetch-tm-register.mjs --url ' + DAILY + 'apc250102.zip');
- console.log('Then build: node scripts/build-tm-index.mjs');
- console.log('');
- console.log('Until a real file is fetched, the index stays a SEED SAMPLE (complete:false)');
- console.log('and the storefront treats it as a partial screen (disclosed in the UI).');
+ console.log('USPTO trademark bulk register — Open Data Portal (data.uspto.gov):');
+ console.log(' Dataset : ' + DATASET + ' (product trtdxfap)');
+ console.log(' NOTE: ODP is behind an AWS WAF + the BSD API needs an ID.me-linked key.');
+ console.log(' A plain headless fetch is BLOCKED. Choose one:');
+ console.log(' (A) node scripts/fetch-tm-register.mjs --file /path/to/downloaded-trtdxf.zip # $0, no key');
+ console.log(' (B) USPTO_ODP_API_KEY=… node scripts/fetch-tm-register.mjs --odp <fileName> # $0, needs key');
+ console.log(' (C) node scripts/fetch-tm-register.mjs --url <direct-https-url>');
+ console.log('Then: node scripts/build-tm-index.mjs && node scripts/build-tm-clearance.mjs');
+ console.log('Until a REAL file is ingested the index stays a SEED SAMPLE (complete:false) and');
+ console.log('NOTHING is sellable — "only legally clear" holds by default.');
return;
}
- const target = get('--url');
- if (!target || !/^https:\/\/bulkdata\.uspto\.gov\//.test(target)) {
- console.error('Refusing: --url must be an https://bulkdata.uspto.gov/... file.');
- process.exit(2);
+
+ // (A) ingest an already-downloaded file (browser / openclaw)
+ const localFile = get('--file');
+ if (localFile) {
+ const out = join(CACHE, basename(localFile));
+ await copyFile(localFile, out);
+ console.log('Ingested local register file -> ' + out + ' (cost: $0)');
+ console.log('Next: node scripts/build-tm-index.mjs && node scripts/build-tm-clearance.mjs');
+ return;
}
- const out = join(CACHE, target.split('/').pop());
- console.log('Cost: $0 (public USPTO bulk data). Fetching', target, '...');
+
+ // (B) BSD download API with an ID.me-linked key
+ const odpFile = get('--odp');
+ if (odpFile) {
+ const key = envVal('USPTO_ODP_API_KEY');
+ if (!key) { console.error('Missing USPTO_ODP_API_KEY (ID.me-linked). See --list option B.'); process.exit(2); }
+ const target = `${BSD_DOWNLOAD}/${encodeURIComponent(odpFile)}/download`;
+ console.log('Cost: $0 (keyed ODP). Fetching', target, '...');
+ const r = await fetch(target, { headers: { 'X-API-KEY': key } });
+ if (!r.ok) { console.error('ODP fetch failed:', r.status, r.statusText, '(WAF or bad key/filename?)'); process.exit(1); }
+ const buf = Buffer.from(await r.arrayBuffer());
+ const out = join(CACHE, basename(odpFile));
+ await writeFile(out, buf);
+ console.log(`Saved ${(buf.length / 1e6).toFixed(1)} MB -> ${out}`);
+ console.log('Next: node scripts/build-tm-index.mjs && node scripts/build-tm-clearance.mjs');
+ return;
+ }
+
+ // (C) direct URL
+ const target = get('--url');
+ if (!target || !/^https:\/\//.test(target)) { console.error('Refusing: --url must be an https URL.'); process.exit(2); }
+ console.log('Cost: $0. Fetching', target, '...');
const r = await fetch(target);
- if (!r.ok) { console.error('fetch failed:', r.status, r.statusText); process.exit(1); }
+ if (!r.ok) { console.error('fetch failed:', r.status, r.statusText, '(the ODP host is WAF-gated; use --file or --odp instead)'); process.exit(1); }
const buf = Buffer.from(await r.arrayBuffer());
+ const out = join(CACHE, basename(new URL(target).pathname) || 'register.xml');
await writeFile(out, buf);
console.log(`Saved ${(buf.length / 1e6).toFixed(1)} MB -> ${out}`);
- console.log('Next: node scripts/build-tm-index.mjs');
+ console.log('Next: node scripts/build-tm-index.mjs && node scripts/build-tm-clearance.mjs');
}
main().catch(e => { console.error(e); process.exit(1); });
← a1b47a6 Add /wear: left-chest signature apparel with USPTO trademark
·
back to CelebritySignatures
·
Add WEAR-READY-FOR-KEY finish note (paste key -> full-regist 4986121 →