# Anytime Mailbox Scraper — Virtual Mailbox Locations + CMRA (`autofacts/anytime-mailbox-scraper-virtual-mailbox-locations-cmra`) Actor

Scrape 2,000+ US + international Anytime Mailbox virtual mailbox locations: street address, operator, starting price. Optional Smarty verification adds CMRA, DPV, county and geocode — a ready CMRA registry for fraud detection, KYC, and address intelligence.

- **URL**: https://apify.com/autofacts/anytime-mailbox-scraper-virtual-mailbox-locations-cmra.md
- **Developed by:** [Richard Feng](https://apify.com/autofacts) (community)
- **Categories:** Lead generation, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Anytime Mailbox Scraper — Virtual Mailbox Locations + CMRA Registry

An Apify actor that crawls **every [Anytime Mailbox](https://www.anytimemailbox.com/) virtual mailbox location worldwide** (2,077 US locations across 52 states and territories as of June 2026, plus international), extracts each street address, mail-center operator, and starting price, and verifies every address through the **Smarty** address-verification APIs — producing deliverability metadata (DPV, **CMRA**, county, geocode, …) per location.

Anytime Mailbox locations are Commercial Mail Receiving Agencies (CMRAs), so `dpv_cmra: "Y"` is expected for valid US addresses — the dataset is effectively a **CMRA registry** with deliverability analysis. USPS publishes no public CMRA list, which is what makes one scraped from the largest virtual-mailbox network useful.

### Use cases

- **Fraud & KYC address screening** — flag sign-ups that use a virtual mailbox instead of a residential address: match against the location list, or use each record's `dpv_cmra` / `pmb_designator` fields directly.
- **Compliance & risk** — CMRA detection is a standard onboarding check for banks, fintechs, lenders, and insurers.
- **Address intelligence & data products** — county, geocode, and deliverability per address, refreshed on a schedule you control.
- **Market & competitor analysis** — operator names and starting prices across the whole network, by state and country.
- **Lead generation** — every location is an independently operated mail center with a named operator.

### How it works (one actor, three phases)

1. **Crawl** — a `CheerioCrawler` walks the `/locations → /l/{country} → /l/{country}/{state}` directory. Each state page's location cards already carry the **full address** (street + city/state/zip), so every location is captured reliably from the listing (≈50 requests for the whole US). With `fetchDetails` on, each `/s/{slug}` detail page is also fetched to add the building suite + mail-center operator — the site soft-404s some of these to `/locations` under load, so they're retried with a fresh session, and the card address always stands.
2. **Incremental enrich** — only **new or changed** addresses are sent to Smarty (US addresses in batches of 100 via the [US Street API](https://www.smarty.com/docs/cloud/us-street-api); international addresses via the [International API](https://www.smarty.com/docs/cloud/international-street-api)). Results are cached in a **named Key-Value Store** that persists across runs, so a weekly refresh never re-bills unchanged addresses (important: the International API costs one credit per address).
3. **Snapshot** — every currently-scraped location is joined with its (cached or fresh) verification and pushed to the run's dataset.

If no Smarty credentials are supplied, phase 2 is skipped and records are emitted with `verification.status: "skipped-no-keys"` — scraping still delivers value on its own.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `startUrls` | array | `[{ url: ".../locations" }]` | Crawl entry points. Narrow to one state (e.g. `/l/usa/rhode-island`) for cheap runs. |
| `maxRequestsPerCrawl` | int | `0` | `0` = unlimited (full site). |
| `maxConcurrency` | int | `10` | Lower if you get blocked. |
| `restrictToCountry` | string | — | Limit the crawl to one country slug, e.g. `usa`. Pair with a matching `startUrl` like `/l/usa`. |
| `fetchDetails` | bool | `true` | Also fetch `/s/` detail pages for the suite + operator. Addresses come from the listings regardless, so turn off for a fast, complete address-only crawl. |
| `searchTerm` | string | — | Free-text grep across every location — see [Search & filter](#search--filter). |
| `searchIsRegex` | bool | `false` | Treat `searchTerm` as a case-insensitive regular expression. |
| `searchFields` | array | all | Limit the search to `slug` / `operator` / `address` / `country` / `url` / `price`. |
| `states` | array | — | US states to keep, as code (`CA`), name (`California`) or slug (`new-york`). Also narrows the crawl. |
| `cities` | array | — | Exact city names, case-insensitive. |
| `countries` | array | — | Country names or directory slugs. |
| `zipcodes` | array | — | Full zipcodes, or shorter prefixes (`902` → all 902xx). |
| `locationType` | enum | `any` | `us`, `international`, or both. |
| `minStartingPrice` / `maxStartingPrice` | int | — | Bounds on the listed starting price in USD. |
| `proxy` | object | Apify Residential, US | Residential US recommended. |
| `enrichWithSmarty` | bool | `true` | Turn off to scrape addresses only. |
| `forceReenrich` | bool | `false` | Re-verify every address, ignoring the cache. |
| `smartyAuthId` / `smartyAuthToken` | secret string | — | Smarty secret-key pair. Falls back to `SMARTY_AUTH_ID` / `SMARTY_AUTH_TOKEN` env vars. |
| `smartyMatch` | enum | `enhanced` | US Street API match strategy. |
| `usCandidates` | int | `1` | Max US candidates per address. |
| `intlGeocode` | bool | `true` | Request lat/long for international addresses. |
| `intlConcurrency` | int | `5` | Parallel international (single-address) lookups. |

### Search & filter

Every criterion is optional, all of them combine with **AND**, and values within one list combine with **OR**. Filtering runs *before* Smarty enrichment, so a narrow query never spends credits on locations you won't receive.

```jsonc
// Every Los Angeles mailbox under $15, run from the site index
{ "startUrls": [{ "url": "https://www.anytimemailbox.com/locations" }],
  "states": ["CA"], "cities": ["Los Angeles"], "maxStartingPrice": 15 }

// Grep for an operator, an address fragment, a zipcode — anything
{ "searchTerm": "pigeon post" }
{ "searchTerm": "broadway suite", "searchFields": ["address"] }
{ "searchTerm": "\\b9\\d{4}\\b", "searchIsRegex": true }
```

**`searchTerm` is the grep**: every word must appear somewhere in the location's text — street, suite, city, state, zipcode, mail-center operator, country, slug, URL and starting price — case-insensitive, or as a regular expression with `searchIsRegex`. Restrict it with `searchFields`. There is no "location type" field on the site itself; the closest equivalents are `locationType` (US vs international), `countries`/`states`, and — once Smarty enrichment is on — the `dpv_cmra` flag on each record.

**Structured filters also make the run cheaper.** `states`, `cities`, `countries`, `zipcodes`, `locationType` and the price bounds are decidable from the listing card alone, so a location they reject never has its detail page fetched — and a `states` filter visits only those state directories (implying `restrictToCountry: "usa"`, which is logged). A verified example: `states: ["CA"] + cities: ["Los Angeles"]` seeded from `/locations` crawled **30 requests** (index → `/l/usa` → `/l/usa/california` → 27 detail pages) instead of the ~2,100 a full US run costs, and returned 27 records.

`searchTerm` is deliberately *not* used for pruning: it can match detail-only fields such as the operator, so a card that looks like a miss may still be a hit. It filters the finished records instead.

Misspelling every value of a filter (e.g. `states: ["Califorina"]`) fails the run immediately rather than quietly returning the whole site.

### Pricing (pay-per-event)

One event, one price: **$0.005 per location returned** — $5.00 per 1,000 results. It uses the platform's built-in `apify-default-dataset-item` event, so every item written to the dataset is charged automatically and there is no charging code to keep in step.

| Run | Locations | Price |
|---|---|---|
| Delaware (the prefilled trial input) | 18 | **$0.09** |
| One state, e.g. California | 390 | **$1.95** |
| Whole US registry | 2,077 | **$10.39** |
| Single address check (filtered) | 1 | **$0.005** |

The price is declared in [`.actor/pay_per_event.json`](.actor/pay_per_event.json), but the Apify **Console → Monetization** page is authoritative — the platform exposes no pricing API.

It is sized for the expensive path. A listing-sourced address costs ~4 KB of proxy traffic, while its detail page costs ~165 KB **and** only works over residential proxy (the datacenter group returns HTTP 403 on `/s/` pages), so a full US crawl with `fetchDetails` on runs about $3.40 of platform usage against $10.39 of revenue. Running with `fetchDetails: false`, or narrowing with the filters above, costs you the same per record but far less to serve.

### Output record

Each dataset item wraps provenance, the address sent to Smarty, and the verification. For US addresses, `verification.report` is exactly the Smarty US Street API response shape.

```jsonc
{
  "source": {
    "provider": "anytimemailbox",
    "url": "https://www.anytimemailbox.com/s/adelanto-11328-bartlett-avenue",
    "slug": "adelanto-11328-bartlett-avenue",
    "operator": "NEXT-N-LINE TAX SERVICES",
    "country": "United States",
    "startingPrice": "US$ 11.99",
    "rawAddress": "11328 Bartlett Ave, Ste 5, Adelanto, CA 92301, United States",
    "scrapedAt": "2026-06-09T…"
  },
  "addressInput": { "street": "11328 Bartlett Ave Ste 5", "secondary": "Ste 5",
                    "city": "Adelanto", "state": "CA", "zipcode": "92301",
                    "country": "United States", "freeform": "…" },
  "verification": {
    "provider": "smarty-us",            // | "smarty-international" | null
    "status": "enriched",               // | "skipped-no-keys" | "no-candidates" | "error"
    "report": { "components": { "pmb_designator": "PMB", … },
                "metadata": { "county_name": "Marin", "latitude": 38.0, … },
                "analysis": { "dpv_cmra": "Y", "dpv_match_code": "Y", … } }
  }
}
```

### Weekly refresh

Schedule the actor in the **Apify Console → Schedules** (e.g. cron `0 6 * * 1` — Mondays 06:00). The enrichment cache lives in the named Key-Value Store `anytimemailbox-enrichment-cache`, which is account-scoped and persists across runs, so each weekly run only spends Smarty credits on locations that are new or changed since last time.

### Observability (Scraper Insight)

Every crawl request reports its outcome (URL, status, latency; terminal failures with a classified status + error message) to the fleet's \[Scraper Insight] dashboard via machine auth — fire-and-forget, so an Insight outage never slows the crawl. Credentials ship in `src/insight.ts` (private repo; rotate + move to env before going public); override with `INSIGHT_URL` / `INSIGHT_APP_ID` / `INSIGHT_APP_SECRET`.

### Development

```bash
npm install
npm run build         # tsc
npm run lint          # eslint
npm test              # vitest (offline unit tests)
npm run start:dev     # run locally (reads storage/key_value_stores/default/INPUT.json)
```

#### Testing with the proxy

Unit tests run offline against saved HTML fixtures in `test/fixtures/`. The live integration tests are **opt-in via environment variables** and route through the Apify proxy (credentials from `../proxy.txt` or `APIFY_PROXY_PASSWORD`):

```bash
## Live crawl test (fetches real pages through proxy.apify.com):
APIFY_PROXY_PASSWORD=<pw> npm test

## Live Smarty verification test:
SMARTY_AUTH_ID=… SMARTY_AUTH_TOKEN=… npm test

## Re-capture HTML fixtures from the live site:
APIFY_PROXY_PASSWORD=<pw> npm run capture-fixtures
```

### Source layout

| File | Responsibility |
|---|---|
| `src/main.ts` | Actor lifecycle + three-phase orchestration |
| `src/routes.ts` | Crawlee router: geo + detail handlers |
| `src/parse.ts` | HTML → structured `ScrapedLocation` / `AddressInput` |
| `src/geo.ts` | Country-slug + US state resolution; US vs international |
| `src/filter.ts` | Search/filter predicate; card-decidable subset used to prune the crawl |
| `src/smarty.ts` | Smarty client (US batch + international single) |
| `src/cache.ts` | Persistent enrichment cache: keying, diff, load/save |
| `src/insight.ts` | Scraper Insight reporting: request outcomes via machine auth |
| `src/enrich.ts` | Partition, verify the diff, build output records |
| `src/spec.ts` | Shared types |

# Actor input Schema

## `startUrls` (type: `array`):

Entry points into the Anytime Mailbox location directory. The prefilled value is one state, so a first run is fast and costs a few cents while showing complete records. Use https://www.anytimemailbox.com/locations to crawl the whole site, or any /l/{country}\[/{state}] page to scope it.

## `maxRequestsPerCrawl` (type: `integer`):

Maximum number of pages to crawl. 0 = unlimited (full site). The prefilled value keeps a first trial run short and cheap — clear it, or set 0, for a complete crawl.

## `maxConcurrency` (type: `integer`):

Max parallel requests. Lower this if you start getting blocked.

## `restrictToCountry` (type: `string`):

Limit the crawl to one country's directory subtree by slug, e.g. 'usa' for US-only (otherwise every country is followed). Pair with a matching startUrl such as https://www.anytimemailbox.com/l/usa.

## `searchTerm` (type: `string`):

Free-text search over each location's text: address, city, state, zipcode, mail-center operator, country, slug, URL and starting price. Multiple words must all appear (e.g. 'los angeles suite'). Case-insensitive. Applied to the finished records, so it also searches detail-page fields such as the operator.

## `searchIsRegex` (type: `boolean`):

Match the search term as a case-insensitive regular expression instead of plain words, e.g. '\b9\d{4}\b' or 'mail ?box'. An invalid pattern fails the run immediately.

## `searchFields` (type: `array`):

Restrict the search term to specific fields. Leave empty to search all of them.

## `states` (type: `array`):

Keep only locations in these US states. Accepts a two-letter code (CA), full name (California) or directory slug (new-york). This also narrows the crawl itself — only the matching state directories are visited, making a single-state run far cheaper. Clear it to crawl every state.

## `cities` (type: `array`):

Keep only locations in these cities (exact name, case-insensitive, e.g. 'Los Angeles'). For partial matches use the search term instead.

## `countries` (type: `array`):

Keep only locations in these countries. Accepts the display name (United Kingdom) or the directory slug (united-kingdom).

## `zipcodes` (type: `array`):

Keep only US locations with these zipcodes. A full 5-digit value matches exactly; anything shorter matches as a prefix, so '902' keeps every 902xx location.

## `locationType` (type: `string`):

Keep only US locations, only international ones, or both.

## `minStartingPrice` (type: `integer`):

Keep only locations whose starting price is at least this many US dollars. Locations with no listed price are excluded when a price bound is set.

## `maxStartingPrice` (type: `integer`):

Keep only locations whose starting price is at most this many US dollars, e.g. 15 for the cheapest mailboxes.

## `proxy` (type: `object`):

Proxies used for crawling. Residential US is recommended.

## `fetchDetails` (type: `boolean`):

Addresses always come from the state listing pages (fast, complete). With this on, each location's /s/ detail page is also fetched to add the building suite + mail-center operator (more requests; the site occasionally soft-404s these under load, which is retried).

## `enrichWithSmarty` (type: `boolean`):

Verify each scraped address via Smarty. If off (or no credentials are provided), the actor only scrapes addresses.

## `forceReenrich` (type: `boolean`):

Re-send every address to Smarty even if already cached. Normally only new or changed addresses are verified.

## `smartyAuthId` (type: `string`):

Smarty secret-key auth-id. Falls back to the SMARTY\_AUTH\_ID environment variable.

## `smartyAuthToken` (type: `string`):

Smarty secret-key auth-token. Falls back to the SMARTY\_AUTH\_TOKEN environment variable.

## `smartyMatch` (type: `string`):

US Street API match mode.

## `usCandidates` (type: `integer`):

Max US Street API candidates to return per address (1-10).

## `intlGeocode` (type: `boolean`):

Request latitude/longitude for international addresses.

## `intlConcurrency` (type: `integer`):

Parallel requests to the Smarty International API (GET, one address each).

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.anytimemailbox.com/l/usa/delaware"
    }
  ],
  "maxRequestsPerCrawl": 25,
  "maxConcurrency": 10,
  "searchIsRegex": false,
  "states": [
    "DE"
  ],
  "locationType": "any",
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  },
  "fetchDetails": true,
  "enrichWithSmarty": true,
  "forceReenrich": false,
  "smartyMatch": "enhanced",
  "usCandidates": 1,
  "intlGeocode": true,
  "intlConcurrency": 5
}
```

# Actor output Schema

## `locations` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://www.anytimemailbox.com/l/usa/delaware"
        }
    ],
    "maxRequestsPerCrawl": 25,
    "maxConcurrency": 10,
    "states": [
        "DE"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("autofacts/anytime-mailbox-scraper-virtual-mailbox-locations-cmra").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [{ "url": "https://www.anytimemailbox.com/l/usa/delaware" }],
    "maxRequestsPerCrawl": 25,
    "maxConcurrency": 10,
    "states": ["DE"],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("autofacts/anytime-mailbox-scraper-virtual-mailbox-locations-cmra").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.anytimemailbox.com/l/usa/delaware"
    }
  ],
  "maxRequestsPerCrawl": 25,
  "maxConcurrency": 10,
  "states": [
    "DE"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call autofacts/anytime-mailbox-scraper-virtual-mailbox-locations-cmra --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,autofacts/anytime-mailbox-scraper-virtual-mailbox-locations-cmra"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/lD0LDTTNDMDd73wxn/builds/MXEIMVlSUQihdFQUn/openapi.json
