# US Importers Finder: ImportYeti Alternative for US Customs Data — API reference

The detail an integrator needs and a buyer does not: linked from the README's Integrations section. `/actor-deploy
--assets` hosts this file next to the images (any other repo file 404s on the Store).

## Tested combinations

Every row is an input the test suite and the review runs start — the combinations that are supported. An input
outside this table is accepted by the schema but not tested; keep the table in sync with `storage-example/` and
`tests/` (gate `check_tested_combinations`, 2026-10-23 company-contact-finder-ai V-27). `sourceMode: "live"` is the
documented live search (`actor` is accepted as an alias).

| Call | What runs | Input |
|---|---|---|
| Daily test / first agent call | the free demo on the sample records, nothing charged | `{}` |
| Demo, as the form opens | the same free demo | `storage-example/INPUT.json` |
| Live search, capped | the source on your account, AI off, ≤ $0.05 | `storage-example/INPUT.live.json` |
| AI on | the same records with the AI verdict (the first e-mail included) | `storage-example/INPUT.ai.json` |
| AI with your own key | the AI through your provider key | `{"enableAi": true, "llmProvider": "byok", "llmModel": "google:gemini-3.5-flash-lite", "llmApiKey": "…"}` + your records |
| Your own list | your rows, no source run | `{"sourceMode": "list", "itemsList": [ … ]}` (fields: README → List-mode rows) |
| A dataset you have | a dataset's items, no source run | `{"sourceMode": "dataset", "datasetId": "<id>"}` |
| Product search, one fallback | zen-studio first (one run per product term), memo23 once if it fails / finds no importer; the HS codes filter and score | `{"sourceMode": "live", "products": ["cotton t-shirts"], "hsCodes": ["6109"], "originCountry": "IN", "maxDiscoveryChargeUsd": 0.5}` |
| HS codes alone | memo23's HS-code pages first (one run for all codes) — never a text query of the digits | `{"sourceMode": "live", "hsCodes": ["9403", "9401"], "originCountry": "VN"}` |
| A brief alone | the product terms and HS codes read from the brief start the live search (`OUTPUT.termsFromBrief`) | `{"sourceMode": "live", "goalDescription": "US apparel brands that import cotton knit t-shirts (HS 6109) in volume", "originCountry": "IN"}` |
| A lane (logistics sales) | importers landing at the ports, at or above the TEU floor, carriers on the row | `{"sourceMode": "live", "products": ["furniture"], "entryPorts": ["savannah", "los angeles"], "minTeu12m": 50, "targetFlags": ["growing"], "originCountry": "VN"}` |
| One memo23 run for a basket | five product tag pages in one source run, one start fee | `{"preset": "india-home-textiles-basket"}` |
| A dataset of any ImportYeti Actor | its importer rows (BOL / lane / supplier rows skipped) | `{"sourceMode": "dataset", "datasetId": "<id>", "products": ["bath towels"], "originCountry": "TR"}` |
| Contact add-on | the best hot / warm importers with a website looked up by `rich_minds/company-contact-finder-ai` on your account (`OUTPUT.contactEnrichment`) | `… "enrichContacts": true, "enrichContactsMax": 10` |
| Value estimate | each row's 12-month weight × your US$/kg | `… "unitValueUsdPerKg": 4.25, "unitValueSource": "UN Comtrade 2025, HS 6109 IN→US"` |

## Cost arithmetic — the one table every page figure is computed from

| Figure | Value | How |
|---|---|---|
| Source row price (zen-studio, the default first source) | $0.00699 per profile + $0.005 per run (one run per product term) | Store pricing, 2026-10-24 |
| Profiles scanned per qualified importer (`rawPerUnit`) | 4 (1 in 4 qualifies — an estimate until the first platform run, 2026-10-30) | `brief.json → pricing.rawPerUnit` |
| Source scan per qualified importer | ≈ $0.028 | 4 × $0.00699 |
| Our event | $0.005 basic · $0.01 AI | `.actor/pay_per_event.json` |
| AI tokens per importer | ≈ $0.0004 (≈ 2,150 in / 510 out with the e-mail, LinkedIn note and day-5 follow-up, measured 2026-10-24 with an own gemini-3.5-flash-lite key, priced at the default google/gemini-2.5-flash-lite rates, $0.10 / $0.40 per M) | `pricing.tokenCostPerUnitUsd` |
| **All in per qualified importer** | **≈ $0.036 rules only · ≈ $0.041 with AI** (the default run's total ÷ its 7 importers, rounded once) | source scan + our event (+ tokens) |
| Default run: 30 profiles → ≈ 7 qualified | $0.25 rules only · $0.29 with AI | $0.21 source (30 × $0.00699 + $0.005) + 7 × $0.005 / 7 × $0.01 + 7 × $0.0004 tokens |
| 100 usable importers (≈ 430 profiles, one run) | $3.50 rules only · $4.05 with AI | $3.01 source + $0.50 / $1.00 + $0.04 tokens |
| Exporter, 5 products weekly, 120 profiles → ≈ 28 | $1.00 rules only · $1.16 with AI a week | $0.86 source (5 runs) + $0.14 / $0.28 + $0.01 tokens |
| Logistics sales, 10 lanes weekly, 300 profiles → ≈ 70 | $2.50 rules only · $2.88 with AI a week | $2.15 source (10 runs) + $0.35 / $0.70 + $0.03 tokens |
| Contact add-on (opt-in) | ≈ $0.0075 per importer with an e-mail found, at most `enrichContactsMax` (10) a run — billed by company-contact-finder-ai on your account, hard-capped at $1 | its own pay-per-event price |
| Free tier | the first 25 qualified importers per Apify account are free (`free-tier`); while the AI is on but unavailable, 5 of them are kept for the first AI-assessed importers (`OUTPUT.chargeSplit.freeKeptForAi`) — the AI reserve | `billing.hold_free_tier` |

## Rivals side by side

The Store neighbours and subscription tools, with the same 100 usable importers priced all in (≈ 430 profiles behind them at 1 in 4.3) — moved here from the README (2026-10-24 review V-33).

| Actor / tool (Store, 2026-10-24) | Users · failed | Price | 100 usable importers, all in | Lacks |
|---|---|---|---|---|
| **This Actor** | new · 0 of 6 | $0.005 / $0.01 AI per qualified importer | $3.50 rules · $4.05 AI | — |
| `memo23/importyeti-scraper` | 25 · 1.9 % | $0.0008 a row + $0.02 a run | ≈ $0.36 for 430 rows, picked by hand | ranking, origin share, pitch |
| `scrapesage/importyeti-scraper` | 24 · 0 % | $0.009 a profile | ≈ $3.87 for 430 profiles, unranked | fit to your product |
| `muhammadafzal/importyeti-scraper` | 2 · — | $0.005 a row + $0.005 a run | ≈ $2.16 for 430 rows, unranked | ranking, pitch |
| `scrapemint/customs-ruling-finder` | 1 | $0.006 a ruling | a different job: CBP rulings | importers |
| ImportGenius · Panjiva · Volza | seat · enterprise | $229 a month (2026-10) · on quote | $229 a month however many you look up | per-result billing, ranking, pitch |
| [ImportYeti website](https://www.importyeti.com/) | free | $0 | $0, ≈ 13 h by hand | ranking, export, API, schedule |

## Run-time formula — one rule for `timeoutSecs`

`timeoutSecs = 180 s (start, push, digest) + source seconds (≈ 2–4 min per 30 profiles, estimate) + the slowest
measured AI pace × importers to assess`. Own-key pace measured 1.4–2.2 s per importer locally (2026-10-24), 1 s and 1.6 s
(2026-10-24) and 8 s (2026-10-23) on the platform with a `google:gemini-3.5-flash-lite` key — size from 8 s: 50 importers ≈ 580 s, 100 ≈ 980 s. The
snippets pass 3240 s, the AI path at the form defaults (`maxQualified` 100 × `aiCandidateMultiplier` 2, two calls at a
time); the run's own `timeoutSecs` is 3600, and `deadline.budget_secs()` stops the AI before it and pushes every row.
A rate-limited own key waits at most 60 s in a run (`AI_BYOK_WAIT_SECS`), then the rest keep their rule score.

## One dataset item

The Harbor & Pine sample row of the demo (`INPUT.ai.json`) with the verdict of the own-key run of 2026-10-24 (`google:gemini-3.5-flash-lite`) — a fictional sample importer, every field as the Actor writes it (the e-mail, LinkedIn note and follow-up are the AI tier's; the rule tier leaves them empty; the contact add-on was off, so the contact fields are null):

```json
{
  "itemId": "f34802ec027f",
  "name": "Harbor & Pine Apparel Co",
  "url": "https://www.importyeti.com/company/sample-harbor-pine-apparel",
  "alsoKnownAs": [],
  "address": "2140 E 7th St, Los Angeles, CA 90021, US",
  "city": "Los Angeles",
  "state": "CA",
  "website": "https://harborpine-apparel.example",
  "domain": "harborpine-apparel.example",
  "phone": null,
  "shipmentsTotal": 412,
  "shipments12m": 64,
  "shipmentsPrior12m": 48,
  "growthPct": 33.3,
  "growthBasis": "source figures: 64 vs 48 shipments (12 m vs 12–24 m)",
  "firstArrival": "2017-03-14",
  "lastArrival": "2026-10-06",
  "weightKg12m": 182400.0,
  "weightBasis": "source figure: kg in the last 12 months",
  "teu12m": 71.5,
  "hsCodes": [
    "6109.10",
    "6110.20"
  ],
  "topSuppliers": [
    {
      "name": "Tiruppur Knit Exports",
      "country": "India",
      "countryCode": "IN",
      "shipments12m": 38,
      "shipmentsTotal": null,
      "hsCodes": [
        "6109.10"
      ]
    },
    {
      "name": "Dhaka Cotton Wear",
      "country": "Bangladesh",
      "countryCode": "BD",
      "shipments12m": 20,
      "shipmentsTotal": null,
      "hsCodes": [
        "6109.10"
      ]
    },
    {
      "name": "Jiangsu Garment Co",
      "country": "China",
      "countryCode": "CN",
      "shipments12m": 6,
      "shipmentsTotal": null,
      "hsCodes": [
        "6110.20"
      ]
    }
  ],
  "topCarriers": [
    {
      "name": "Maersk Line",
      "code": "MAEU",
      "shipments12m": 40,
      "shipmentsTotal": null
    }
  ],
  "originSharePct": 59.4,
  "originShareBasis": "the 3 listed suppliers' 12-month shipments",
  "buysFromOrigin": true,
  "chinaSharePct": 9.4,
  "uflpaExposed": null,
  "entryPorts": [
    "Los Angeles, Calif",
    "Long Beach, Calif"
  ],
  "goodsDescriptions": [
    "100% COTTON KNITTED MENS T-SHIRTS",
    "LADIES COTTON CREW NECK TEE",
    "COTTON PULLOVER SWEATSHIRTS"
  ],
  "estValueUsd12m": null,
  "valueBasis": null,
  "contactRoute": "https://harborpine-apparel.example",
  "companyType": "brand",
  "flags": [
    "hs_match",
    "keyword_match",
    "buys_from_origin",
    "growing",
    "active_recently",
    "has_contact"
  ],
  "targetFlagsMatched": [],
  "ruleScore": 93,
  "score": 93,
  "label": "hot",
  "fitScore": 93,
  "fitReason": "\"100% COTTON KNITTED MENS T-SHIRTS\" matches the exporter's cotton t-shirts.",
  "pitchAngle": "You already source 59.4% of your shipments from India across 64 shipments in the last 12 months. With your volume growing 33.3%, we can expand your cotton t-shirt supply directly from India.",
  "scoreReasons": [
    "Imports cotton t-shirts matching HS 6109",
    "64 shipments in the last 12 months with 33.3% growth",
    "Already buys 59.4% of its shipments from India",
    "Active recently with last arrival on October 6, 2026"
  ],
  "summary": "Harbor & Pine Apparel Co is a Los Angeles-based brand that imported 64 shipments in the last 12 months, showing 33.3% growth and 59.4% origin share from India.",
  "generatedText": "You already source 59.4% of your shipments from India across 64 shipments in the last 12 months. With your volume growing 33.3%, we can expand your cotton t-shirt supply directly from India.",
  "recommendedNextStep": "Check its Tiruppur Knit Exports supplier details on its ImportYeti page, then e-mail the sourcing team via its website.",
  "emailSubject": "Expanding your cotton t-shirt supply from India",
  "emailDraft": "Hi Sourcing Team, I noticed Harbor & Pine Apparel Co imported 64 shipments in the last 12 months, with 59.4% already coming from India. We manufacture premium cotton t-shirts in India, offering reliable quality with a {{lead time}} lead time. Could we send samples to evaluate for your upcoming lines? Best, {{your name}} {{your company}}",
  "linkedinNote": "Hi, I saw Harbor & Pine Apparel Co imported 64 shipments recently with 59.4% originating from India. We supply premium cotton t-shirts from India and would love to connect with your sourcing team.",
  "followUpDraft": "Hi, following up on my previous note. Since your shipments grew 33.3% to 64 in the last 12 months, we would love to support your expanded volume with our cotton t-shirts. Can we schedule a brief call this week? Best, {{your name}}",
  "contactEmail": null,
  "contactEmailRole": null,
  "contactName": null,
  "contactTitle": null,
  "aiModel": "google:gemini-3.5-flash-lite",
  "assessedBy": "ai",
  "sourceActor": "list",
  "chargedEvent": "demo",
  "billedAs": "qualified-us-importer-ai",
  "dedupeKey": "iy:sample-harbor-pine-apparel",
  "source": {
    "mode": "list"
  },
  "scrapedAt": "2026-10-24T12:43:08+00:00"
}
```

## Every field of a row

| Field | Description |
|---|---|
| `itemId`, `name`, `url`, `alsoKnownAs`, `address`, `city`, `state`, `website`, `domain`, `phone`, `contactRoute` | The importer, its ImportYeti page, its website as an https URL (the bare host in `domain`), how to reach it first (website → phone → page) |
| `companyType` | brand / retailer / distributor / manufacturer / forwarder / unknown — the AI's, else name keywords; an ambiguous name ("Express", "… Shipping Co") stays `unknown` with the `forwarder` flag |
| `shipmentsTotal`, `shipments12m`, `shipmentsPrior12m`, `growthPct`, `growthBasis` | Shipments all years, last 12 months, the source's own prior-12-month figure; growth in % from two figures of the same list and period — `growthBasis` says which (the source's pair, its shipping lanes, its carriers, its quarterly figure); null when no pair exists |
| `firstArrival`, `lastArrival` | The customs records' own arrival dates (the source's publication), not the time this run downloaded them (`scrapedAt`) |
| `weightKg12m`, `weightBasis`, `teu12m` | Kg in 12 months — the source's figure or a prorated estimate (`weightBasis` says which) — and containers |
| `hsCodes`, `goodsDescriptions`, `entryPorts` | What it imports (codes, its bills' own words) and the US ports it lands at |
| `topSuppliers`, `topCarriers` | Top 10 suppliers (name, country, countryCode, shipments12m, shipmentsTotal, hsCodes) and top 5 ocean carriers (name, SCAC code, shipments12m, shipmentsTotal) |
| `originSharePct`, `originShareBasis`, `buysFromOrigin`, `chinaSharePct`, `uflpaExposed` | Share of shipments from your country and from China — over the source's per-country totals of every shipment when it has them, else over every listed supplier (`originShareBasis` says which); null when unknown; the source's UFLPA flag |
| `estValueUsd12m`, `valueBasis` | Weight × your US$/kg benchmark, an estimate with its basis; null without a benchmark |
| `flags`, `targetFlagsMatched`, `ruleScore`, `fitScore`, `score`, `label`, `assessedBy` | The README's flag list; `score` = the AI fit when assessed, else the rule score; `assessedBy` ai / sample / rules |
| `fitReason`, `pitchAngle`, `recommendedNextStep`, `scoreReasons`, `summary`, `generatedText` | Why it fits (quoting its goods with AI), the opener spoken to the importer, the advice to you, the AI's notes; `generatedText` = `pitchAngle` (the cross-Actor opener column) |
| `linkedinNote`, `followUpDraft` | The AI tier's LinkedIn connection note (≤ 300 characters, no links) and the day-5 follow-up e-mail (≤ 60 words, signed `{{your name}}`) — empty on the rule tier and on cold rows |
| `contactEmail`, `contactEmailRole`, `contactName`, `contactTitle` | The contact add-on (`enrichContacts`): the checked e-mail found on the importer's website by company-contact-finder-ai in this run, its inbox role, the first person named on the team page and their title — null when the add-on is off or found none; `contactRoute` then starts with `mailto:` |
| `emailSubject`, `emailDraft` | The AI tier's first e-mail: subject (≤ 10 words) and body (≤ 90 words) with `{{your name}}` / `{{your company}}` / `{{lead time}}` placeholders, grounded in the row; empty on the rule tier and on cold rows |
| `aiModel`, `sourceActor`, `source`, `chargedEvent`, `billedAs`, `dedupeKey`, `scrapedAt` | Provenance (which Actor / mode and the search term that found the row), billing (`billedAs` keeps the priced tier when `chargedEvent` is `free-tier` / `demo`), the upsert key, when this run downloaded the row (UTC) |

## `OUTPUT` record keys

Merged from three local runs on 2026-10-24 — list mode with the AI on (the demo sample), a scheduled second run of one search (memory, trend, history, webhook, running log) and a live search refused for the plan — so every key has a type and one example; `null` means the key is absent in that run.

| Key | Type(s) | Example (from) | Notes |
|---|---|---|---|
| `mode` | str | `"list"` (list + AI) | `demo` · `live` · `list` · `dataset` — what this call was (the 'Which call runs what' table) |
| `qualified` | int | `9` (list + AI) |  |
| `candidates` | int | `9` (list + AI) |  |
| `stats` | dict | `{discovered: int, skipped_forwarder: int, skipped_beyond_ai_pool: int, ai_second_pass: int, ai_failures: int, ai_assessed: int, ai_sample_verdicts: int, skipped_below_min_score: int…}` (list + AI) |  |
| `skips` | dict \| null | `{forwarder: list}` (list + AI) | the names each free rule dropped, per reason (`forwarder`, `not_us_importer`, `port_not_watched`, …) |
| `chargedEvents` | dict | `{}` (list + AI) |  |
| `unchargedEvents` | null | — |  |
| `freeTierThisRun` | int | `0` (list + AI) |  |
| `demoRows` | int \| null | `9` (list + AI) |  |
| `freeTierRemaining` | null | — |  |
| `freeAiReserve` | null | — |  |
| `chargeSplit` | null | — | demo / free / basic / ai rows of the run, `freeKeptForAi` the AI reserve (PPE runs only) |
| `demoIgnoredFields` | null | — |  |
| `switchedToLive` | null | — |  |
| `demoTextsDropped` | null | — |  |
| `demoTextsSearched` | null | — |  |
| `budgetLimitReached` | bool | `false` (list + AI) |  |
| `targetFlags` | null | — |  |
| `yieldByQuery` | dict \| null | `{yoga mat: dict, -: dict}` (scheduled #2) |  |
| `webhook` | dict \| null | `{sent: int, failed: int, failedKeys: list, redelivered: int, pendingNextRun: int, signed: bool}` (scheduled #2) | sent / failed / failedKeys / redelivered / pendingNextRun / signed — null without `webhookUrl` |
| `dedupeStore` | dict \| null | `{name: str, scope: null, remembered: int, newMemory: bool, otherSearchesRemembered: null}` (scheduled #2) |  |
| `aiError` | str \| null | `"the key's quota looks used up — no call answered in 20 s of rate-limit backoff; retry …` (list + AI) |  |
| `trend` | dict \| null | `{prevAt: str, qualified: dict, hot: dict, originBuyers: dict, switchCandidates: dict, chinaPlusOne: dict}` (scheduled #2) | deltas vs the previous run of the same search; null on the first run |
| `history` | list \| null | `[2 × dict] e.g. {"at": "2026-10-24T05:01:37+00:00", "qualified": 3, "hot": 0, "originB` (scheduled #2) | every delivering run of this search, oldest first (qualified, hot, originBuyers, switchCandidates, chinaPlusOne) |
| `historyDataset` | dict \| null | `{name: str, appended: int}` (scheduled #2) |  |
| `scheduledSizing` | dict \| null | `{maxImporters: dict, maxDiscoveryChargeUsd: dict}` (scheduled #2) |  |
| `firstRunBackfill` | null | — |  |
| `scanCapHit` | null | — |  |
| `notDeliveredCap` | null | — |  |
| `seedsRolled` | null | — |  |
| `presetApplied` | null | — |  |
| `contactEnrichment` | dict \| null | `{actor: str, requested: int, found: int, priceUsdPerContact: float}` | the contact add-on's run — `error` instead of `found` when it could not run (a plan that cannot run Store Actors) |
| `termsFromBrief` | null | — | the search terms read from `goalDescription` when `products` / `hsCodes` were empty |
| `aiCoverage` | dict \| null | `{aiAssessed: int, ruleScored: int, model: str, aiRejected: null, secondPass: null, sampleVerdicts: int, fallbackReasons: null}` (list + AI) | AI-assessed vs rule-scored over the delivered rows; `fallbackReasons` sum to `ruleScored` |
| `aiTrial` | null | — |  |
| `aiTokens` | null | — |  |
| `ok` | bool | `true` (list + AI) | false only when the source was refused / failed and nothing was loaded |
| `outcome` | str | `"ok"` (list + AI) | `ok` · `empty` · `source_refused` · `source_failed` · `error` (the Run-outcomes table rows) |
| `sourceError` | str \| null | `"Source failed (zen-studio/importyeti-scraper): ForbiddenError: You cannot run this pub…` (refused live) |  |
| `sourceRefused` | str \| null | `"plan"` (refused live) | `"plan"` when the buyer's plan cannot run the source Store Actor |
| `sampleFallback` | null | — |
| `sampleNoMatch` | bool \| null | `true` (refused, no sample importer of your product) | the Console fallback found no sample importer of your product / HS code — 0 rows, the status names both ways on |  |
| `sourceStatus` | dict \| null | `{nonImporterRows: int, rowsSkippedUnmappable: int, unmappedFields: dict}` (scheduled #2) | the live search's path (which Store Actor ran, ok, rows / error), status, partial, spentUsd — null for the demo and list mode |
| `demo` | null | — |  |
| `timing` | dict | `{sourceSecs: float, aiSecs: float, aiSecsPerUnit: null, aiBackoffSecs: float, aiPaceSecs: null, totalSecs: float}` (list + AI) | seconds per phase — size `timeoutSecs` from it (sourceSecs, aiSecs, aiSecsPerUnit, aiBackoffSecs, aiPaceSecs, totalSecs) |
| `scheduleInput` | dict | `{sourceMode: str, products: list, hsCodes: list, originCountry: str, goalDescription: str, itemsList: list, enableAi: bool, minScore: int…}` (list + AI) |  |
| `timeSaved` | str \| null | `"Turn on the AI (`enableAi`): your 1 hot / warm US importers would get a fit verdict ag…` (scheduled #2) |  |
| `reviewAsked` | null | — |  |
| `importers` | dict | `{byType: dict, originBuyers: int, switchCandidates: int, chinaPlusOne: int}` (list + AI) |  |
| `reportUrl` | null | — |  |
| `nextSteps` | list \| null | `[3 × str] e.g. "AI: No call answered in 20 s of rate-limit backoff; retry later, or u` (list + AI) |  |
| `digestEmailed` | bool | `false` (scheduled #2) |  |
| `refusalAlerted` | null | — |  |

### Nested records

- `stats` — `discovered` int, `skipped_forwarder` int, `skipped_beyond_ai_pool` int, `ai_second_pass` int, `ai_failures` int, `ai_assessed` int, `ai_sample_verdicts` int, `skipped_below_min_score` int, `ai_rejected` int, `capped_by_max_qualified` int, `skipped_seen_in_previous_run` int
- `skips` — `forwarder` list
- `yieldByQuery` — `yoga mat` dict, `-` dict
- `webhook` — `sent` int, `failed` int, `failedKeys` list, `redelivered` int, `pendingNextRun` int, `signed` bool
- `dedupeStore` — `name` str, `scope` null, `remembered` int, `newMemory` bool, `otherSearchesRemembered` null
- `trend` — `prevAt` str, `qualified` dict, `hot` dict, `originBuyers` dict, `switchCandidates` dict, `chinaPlusOne` dict
- `history` — `[].at` str, `[].qualified` int, `[].hot` int, `[].originBuyers` int, `[].switchCandidates` int, `[].chinaPlusOne` int
- `historyDataset` — `name` str, `appended` int
- `scheduledSizing` — `maxImporters` dict, `maxDiscoveryChargeUsd` dict
- `aiCoverage` — `aiAssessed` int, `ruleScored` int, `model` str, `aiRejected` null, `secondPass` null, `sampleVerdicts` int, `fallbackReasons` null
- `sourceStatus` — `nonImporterRows` int, `rowsSkippedUnmappable` int, `unmappedFields` dict
- `timing` — `sourceSecs` float, `aiSecs` float, `aiSecsPerUnit` null, `aiBackoffSecs` float, `aiPaceSecs` null, `totalSecs` float
- `scheduleInput` — `sourceMode` str, `products` list, `hsCodes` list, `originCountry` str, `goalDescription` str, `itemsList` list, `enableAi` bool, `minScore` int, `maxQualified` int, `mostRecentWithinMonths` int, `dedupeAcrossRuns` bool
- `importers` — `byType` dict, `originBuyers` int, `switchCandidates` int, `chinaPlusOne` int
