Create Bulk Scans
Create scans for a URL across multiple countries at once.
Request
- Base URL: https://app.kaminari.ad
- URL: https://app.kaminari.ad/api/v1/scans/bulk
- Auth: HTTP bearer
Request body
Ad tag to check: an HTML snippet OR an http(s) URL of a page with the creative already rendered. Mutually exclusive with url/vast_tag.
VAST video tag to check: an http(s) URL of a VAST endpoint OR raw VAST XML. Mutually exclusive with url/ad_tag.
Optional http(s) page URL the check is performed from. With ad_tag or vast_tag it is the page the tag is embedded in: the browser commits the harness document on this URL without fetching the publisher, so the creative is embedded exactly as it would be on that page. With url or ad_discovery it is where the visitor came from and travels as the Referer of the page request. Cross-origin subrequests receive the origin only (https://publisher.example/, no path) under Chromium's default strict-origin-when-cross-origin policy — same as on a real publisher. Must be a publicly routable host, carry no credentials, and stay within 2048 characters once normalised.
Skips detection and tagging on the first N unique domains of the redirect chain, counted from the entry point. Use it to exclude your own click/tracking domains so they don't trigger tags or alerts. Domains fold to their registrable form, so www.example.com and example.com count as one. Any other request to one of those domains is excluded too, wherever in the page it was made from. Default 0 (nothing skipped). Maximum 5.
Campaign to attribute the scan to. Alerts are raised only for scans that carry a campaign whose policy set has rules.
Publisher ad discovery: detect ad blocks on the page and spawn one child scan per detected ad. Only valid with url (not ad_tag/vast_tag).
How many ad blocks to look for on the publisher page before stopping. Each detected ad becomes its own scan with its own report, and is billed as a separate check. Only valid together with ad_discovery. Omit to use the platform default. Maximum 25.
Which ad formats the discovery run captures: banner, video and/or pop (pop covers both pop-unders and tab-unders), each at most once. A format left out is neither captured nor billed — e.g. ['pop'] checks the page's pops without screenshotting or clicking its banners; a tab-under the page forces on its own is still reported as a pop. Only valid together with ad_discovery. Omit to capture every format.
How many times to scan this combination. Each repeat is a full scan with its own report and its own billing.
Browser-session isolation mode for a group of repeated scans.
ISOLATED is the historical (and only) crawler behaviour: every scan gets its own Chromium, its own context and its own proxy session, so every repeat sees a new IP and an empty cookie jar.
SHARED makes the crawler run the whole group inside one browser session — one proxy session (one IP) and storage_state carried from one repeat to the next. Only groups in this mode carry a Scan.repeat_session_id.
Extra crawl attempts after a technical failure (dead proxy, timeout, browser crash). The same scan is reused, so a retry is never billed twice.
ISO 3166-1 alpha-2 country codes, case-insensitive; stored uppercase.
Response
Successful Response
Status of a scan through its lifecycle.
Ordered: pending(0) → running(1) → crawled(2) → checking(3) → checking_async(4) → terminal(5). Forward-only transitions enforced by advance_status() in the DB (see status_order() Postgres function).
CHECKING_ASYNC means all inline parts (regexp + domain checks) have been applied and we are waiting only on async parts (LLM batch today, SafeBrowsing / VirusTotal tomorrow). UI can show partial tags + alerts at this stage; only classification is still pending.
RECHECKING is a transient state used only by the recheck flow. The recheck use case atomically moves a scan from a terminal state into RECHECKING, wipes prior tags (CH tombstones), wipes scan_part_state, then atomically transitions to CHECKING with fresh expected_parts. Both transitions bypass the forward-only advance_status() guard via dedicated repository methods.
Why RECHECKING exists: stale check.parts.* messages for a now- rechecking scan must NOT write tags (those would survive the wipe without tombstones, corrupting the post-recheck tag set). Consumers check WRITE_BLOCKED_STATUSES and skip the message when the scan is in RECHECKING. Defence in depth — the TagWriterAdapter's UoW also re-reads the scan status before save.
The sibling scans this request also created, excluding this one. Populated only by the create endpoints — list and detail responses leave it empty.
Changes
No recorded changes to this operation across all 1 revision of this API.