DEV Community

Devil Scrapes
Devil Scrapes

Posted on

ImportYeti Alternative: the date field that quietly swaps month and day

Quick answer

importyeti.com's public search API exposes a shipment-date field formatted DD/MM/YYYY — not the US-standard MM/DD/YYYY you'd naturally assume on a site that only covers US Customs filings. Parse it with the wrong format string and every date for the first 12 days of a month silently swaps month and day. The ImportYeti Alternative Scraper parses it correctly, confirmed against the live wire format, and returns 15 typed fields per matched importer or supplier at $0.005 per row plus a $0.005 run-start charge — $5.005 for 1,000 rows.

The date bug that only shows up half the time 🗓️

This is the kind of bug that survives code review, because it doesn't fail loudly — it fails quietly, on some rows.

ImportYeti's /api/search endpoint returns a mostRecentShipment field as a plain string like "15/07/2026". If you're a US-focused team scraping a US Customs data source, the reasonable-looking assumption is MM/DD/YYYY. For that string, strptime("15/07/2026", "%m/%d/%Y") throws immediately — day 15 isn't a valid month, so at least you find out. But for "03/07/2026", the wrong format string doesn't throw. It happily parses to March 7th when the real date is July 3rd. No exception, no warning, just a wrong date sitting in your dataset looking exactly as valid as the correct ones.

We confirmed the actual format live against the API — it's DD/MM/YYYY — and parse against that. When a value doesn't match even that format, we log a warning and null the field rather than guess. most_recent_shipment_date in the output is either a correct ISO date or null; it's never a plausible-looking wrong one.

What else the live API doesn't tell you upfront 🔧

A few more things we only know because we captured the live wire format rather than guessing from the rendered page.

The query parameter has to be q. query and search both return a stripped-down envelope with fewer fields. q returns the richest one — the one with totalShipments, otherAddressesCount, and the rest of the profile data this Actor's output depends on.

Pagination is 1-indexed, confirmed by a live cloud run where a 15-result request returned ranks 1 through 15, with results 11–15 sourced from page=2 — meaning page 1 is a real, distinct page and starting your loop at page=0 gets you nothing extra, just a wasted request.

country has no source field. The search envelope carries countryCode (ISO-3166-1 alpha-2) but never a full country name. Rather than reverse-mapping a code to a name with our own lookup table and risking a mismatch, country ships as null and country_code carries the verified value.

The API tells you your own rate-limit budget, in-band. A requestLimitRemaining counter rides inside the JSON response body itself — not a header — starting around 24 and counting down. We treat a low remaining budget the same way we'd treat an approaching 429: pause, rotate session and browser fingerprint, and resume, instead of waiting to actually get blocked.

What we handle for you 🛡️

  • Residential proxy by default, not opt-in. importyeti.com issues Cloudflare managed challenges to flagged datacenter IPs on every probed path — this is the one Actor in our fleet where RESIDENTIAL ships as the default proxy group instead of something you have to turn on.
  • We detect the challenge two ways — a cf-mitigated: challenge response header, or a "Just a moment" body marker as a fallback — and rotate to a fresh session and browser profile before retrying, rather than treating a 200-with-challenge-page as real data.
  • We retry with exponential backoff on 408 / 429 / 5xx, up to 5 attempts, honoring Retry-After.
  • We rotate browser fingerprints across Chrome, Firefox, and Safari TLS impersonation profiles via curl-cffi — header-only spoofing doesn't survive Cloudflare's fingerprinting, so we don't rely on it.
  • We deduplicate by URL slug. The same company matched by two different search queries is written and charged once, not twice.
  • You pay only for results that hit your dataset. No data, no charge, beyond the small run-start warm-up fee.

Full output schema 📦

Fifteen fields per matched company row:

Field Type Notes
company_name string Legal name from US Customs filings
company_type enum importer (US consignee) or supplier (foreign shipper)
country string | null No source field in the live envelope — always null
country_code string \ null
address string | null Most recent customs record address
total_shipments int | null Total bill-of-lading count on file
most_recent_shipment_date string \ null
other_addresses_count int | null Distinct alternative addresses on file
other_names_count int | null Distinct alternative names (DBAs, subsidiaries)
trademarks list[string] Linked brand names
profile_url string Canonical `importyeti.com/company\
{% raw %}slug string URL slug — dedup key
source_query string | null Which search query matched, in search mode
rank int | null Position in the search-API ranking (1 = top)
scraped_at string ISO 8601 UTC

Who this is for

Sales prospecting — build a list of US importers already shipping a competitor's product category, ranked by shipment volume via total_shipments.

Sourcing research — find foreign suppliers already shipping into the US for a given product before cold-emailing a trade show floor.

M&A and supply-chain diligence — sanity-check a target company's declared trade volume and country exposure in minutes. trademarks pairs well with a targeted pull from our USPTO Trademark Scraper when you need to verify brand ownership too.

Frequently asked questions

Is this the same data as ImportYeti, Panjiva, or ImportGenius?
It's a self-serve wrapper around importyeti.com's own keyless JSON search — the same underlying US Customs bill-of-lading data those $99–$399+/mo subscription tools charge for, aggregated into company/supplier trade-profile rollups, not per-shipment line items.

Why is most_recent_shipment_date sometimes null?
Only when the raw value doesn't match the confirmed DD/MM/YYYY format. We log the malformed value and null the field rather than fabricate a date.

Does it need residential proxies to work?
Yes, and it's on by default for this Actor specifically — importyeti.com has confirmed Cloudflare managed-challenges on datacenter IPs across every probed path.

What does 2,000 matched rows cost?
$10.005 — 2,000 × $0.005, plus the $0.005 run-start charge.

Can I get individual per-shipment records (date, HS code, weight, port)?
Not in v1. Those live behind importyeti.com's login/bot-gated profile pages. This Actor covers the public rollup data — shipment counts and most-recent activity, not line-item filings.

Try it

Live on the Apify Store: ImportYeti Alternative Scraper.

Feed it company names, product keywords, or direct profile URLs — it resolves each to a matched, deduplicated trade-profile row. Pay-per-event, no subscription.


Built by Devil Scrapes — we build scrapers for the targets that fight back.

Top comments (0)