# Instacart Public Retailer Phone Scraper (`w3crawler/instacart-phone-number-scraper`) Actor

Extract bounded publicly displayed retailer business phone numbers from supplied Instacart pages with explicit access diagnostics.

- **URL**: https://apify.com/w3crawler/instacart-phone-number-scraper.md
- **Developed by:** [w3crawler](https://apify.com/w3crawler) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 retailer phones

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does Instacart Public Retailer Phone Auditor do?

This Actor audits caller-supplied public [Instacart retailer and location pages](https://www.instacart.com/store) for a phone number that the page explicitly publishes as a retailer-business contact. It reads public JSON-LD, `tel:` links, and clearly labelled visible text. A normal row is emitted only when the source page provides the phone evidence and the record retains its canonical source, final URL, extraction method, evidence context, confidence classification, and publication flags.

The Actor does not promise that every Instacart page has a phone. If the supplied page has no qualifying public phone, the run emits a bounded `NO_PUBLIC_PHONE` diagnostic. It never infers a generic retailer number, copies a directory or reader result, treats Instacart support as a retailer phone, or substitutes a cached or mirrored page for the supplied Instacart source.

This Actor does not log in, use customer profiles, submit credentials, reuse cookies, solve CAPTCHAs, bypass security pages, or call private Instacart APIs. A separately authorized first-party or business-location API is outside this Actor's current contract.

### Why use Instacart Public Retailer Phone Auditor?

- Audit which retailer-business phone information a specific public Instacart contact or location page discloses.
- Build a bounded evidence-preserving contact list from pages supplied by the caller.
- Detect changes in a public retailer page with Apify scheduling, monitoring, API access, and integrations.

The input is intentionally caller-supplied. There is no default retailer page that is treated as phone evidence. The current public Kroger routes tested for this Actor returned usable pages without a qualifying retailer phone, so an empty result is an honest outcome rather than a placeholder.

### How to run the Actor

1. Add one or more public HTTPS Instacart retailer, storefront, or location contact pages to `contactUrls`.
2. Keep `fixture` false for a live public run and choose bounded limits and timeouts.
3. Run the Actor, then inspect the dataset and `OUTPUT_SUMMARY` key-value record.

The Actor fetches each supplied URL once, follows only a bounded redirect chain that remains on an official Instacart host, and removes duplicate normalized URLs and phone identities. Product paths are ignored because contact text there may belong to a manufacturer. The `maxItems` limit applies to normal phone records; diagnostics do not consume it.

### Input

`contactUrls` is the primary input. Each item is an object with a required `url` string. URLs must be absolute HTTPS URLs on an Instacart host and must identify a retailer/location path such as `/retailer/...` or `/store/...`. Credentials, arbitrary external domains, and product paths are rejected or reported as diagnostics.

| Field | Type and bounds | Description |
| --- | --- | --- |
| `contactUrls` | array of `{ "url": string }` objects | Caller-supplied public retailer/location contact pages. Default is an empty array; a useful run must supply at least one URL. |
| `startUrls` | array of `{ "url": string }` objects | Compatibility alias for `contactUrls`. |
| `urls`, `url` | array/object and string aliases | Additional compatibility aliases for caller-supplied pages. |
| `maxItems` | integer 1–100; default 50 | Maximum normal phone records. Diagnostics do not consume this limit. |
| `timeoutMs` | integer 5,000–120,000; default 30,000 | Per-request timeout. |
| `maxBytes` | integer 100,000–5,000,000; default 2,000,000 | Maximum public response size retained. |
| `requestDelayMs` | integer 0–10,000; default 0 | Delay before a retry or redirect. The first request is sent immediately. |
| `maxRequestRetries` | integer 0–2; default 1 | Retries only timeouts, transport failures, and HTTP 5xx responses. |
| `userAgent` | string up to 300 characters | Optional transparent user-agent string. Credentials and control characters are rejected. |
| `proxyConfiguration` | object or null | Optional Apify Proxy settings. Direct access is the default; proxy use does not bypass access controls. |
| `fixture` | boolean; default false | Checked-in parser QA only. Fixture records never qualify as live evidence. |

#### Live input

```json
{
  "contactUrls": [
    {
      "url": "https://www.instacart.com/retailer/kroger-delivery/tx/near-me-in-telephone-tx"
    }
  ],
  "maxItems": 2,
  "timeoutMs": 15000,
  "maxBytes": 1000000,
  "maxRequestRetries": 0,
  "fixture": false
}
```

This is a valid audit input. It may return `NO_PUBLIC_PHONE` because the page must be the source of the phone; the Actor does not fill the gap from another website.

#### Multiple caller-supplied pages with standard proxy

```json
{
  "contactUrls": [
    {
      "url": "https://www.instacart.com/retailer/kroger-delivery/tx/near-me-in-telephone-tx"
    },
    {
      "url": "https://www.instacart.com/store/kroger/storefront"
    }
  ],
  "maxItems": 10,
  "timeoutMs": 30000,
  "maxBytes": 2000000,
  "requestDelayMs": 500,
  "maxRequestRetries": 1,
  "fixture": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

`apifyProxyGroups` and `apifyProxyCountry` are optional account-enabled settings. The Actor does not use stealth, fingerprint evasion, CAPTCHA solving, login bypass, or private cookies. If the proxy cannot be initialized, the run records `INVALID_PROXY`.

#### Compatibility aliases

```json
{
  "startUrls": [
    {
      "url": "https://www.instacart.com/store/kroger/storefront"
    }
  ],
  "url": "https://www.instacart.com/retailer/kroger-delivery/tx/near-me-in-telephone-tx",
  "maxItems": 5,
  "maxRequestRetries": 0,
  "fixture": false
}
```

#### Local fixture QA

```json
{
  "contactUrls": [
    {
      "url": "https://www.instacart.com/store/fresh-market"
    }
  ],
  "maxItems": 5,
  "maxRequestRetries": 0,
  "fixture": true
}
```

Fixture mode reads the checked-in HTML fixture and proves only parser/schema behavior. Its phone number is not a live Instacart result and is never qualification evidence.

### Output

Normal dataset rows use `recordType: "instacart-retailer-phone"` and `contactScope: "retailer-public-business"`. Required provenance fields include `sourceUrl`, `canonicalUrl`, `finalUrl`, `sourceDomain`, `evidenceContext`, `extractionMethod`, `confidence`, `publicationStatus`, and `isPublic`. Missing source values are omitted; no retailer identity, address, or phone is inferred.

#### Illustrative normal record

Values below are synthetic examples and are not Cloud evidence.

```json
{
  "recordType": "instacart-retailer-phone",
  "contactScope": "retailer-public-business",
  "recordId": "phone_0123456789abcdef0123",
  "source": "Instacart",
  "url": "https://www.instacart.com/store/example-market",
  "sourceUrl": "https://www.instacart.com/store/example-market",
  "canonicalUrl": "https://www.instacart.com/store/example-market",
  "finalUrl": "https://www.instacart.com/store/example-market",
  "sourceDomain": "www.instacart.com",
  "retailerUrl": "https://www.instacart.com/store/example-market",
  "retailerName": "Example Market",
  "phoneNumber": "+1 (212) 555-0134",
  "phoneDigits": "12125550134",
  "phoneSource": "json-ld",
  "evidenceContext": "JSON-LD telephone",
  "extractionMethod": "json-ld.telephone",
  "confidence": "high",
  "publicationStatus": "explicit-public-business-contact",
  "isPublic": true,
  "scrapedAt": "2026-09-08T00:00:00.000Z",
  "retrievedAt": "2026-09-08T00:00:00.000Z"
}
```

`confidence` is a source-strength classification, not a probability: JSON-LD and `tel:` evidence is `high`, while clearly labelled visible public text is `medium`. `isPublic` means the phone was visible without credentials in the fetched page; it is not a legal privacy determination.

#### Exact diagnostic record

```json
{
  "url": "https://www.instacart.com/retailer/example-delivery/tx/near-me-in-example-tx",
  "error": "No public retailer phone number was exposed in the bounded page response.",
  "errorCode": "NO_PUBLIC_PHONE",
  "scrapedAt": "2026-09-08T00:00:00.000Z"
}
```

Diagnostics use only `url`, `error`, `errorCode`, and `scrapedAt`. Common codes are `CONTACT_URL_REQUIRED`, `INVALID_INPUT`, `NO_PUBLIC_PHONE`, `BLOCKED`, `LOGIN_REDIRECT`, `HTTP_ERROR`, `TIMEOUT`, `REQUEST_FAILED`, `RESPONSE_TOO_LARGE`, and `INVALID_PROXY`.

#### OUTPUT_SUMMARY

Operational counts and route information are stored in `OUTPUT_SUMMARY`. `OUTPUT` is also written as a compatibility alias.

```json
{
  "status": "NO_PUBLIC_DATA",
  "source": "Instacart",
  "actor": "instacart-phone-number-scraper",
  "targetCount": 1,
  "itemCount": 1,
  "phoneRecordCount": 0,
  "normalRecordCount": 0,
  "diagnosticCount": 1,
  "blockedCount": 0,
  "dataAvailable": false,
  "sourceTransport": "direct",
  "inputMode": "caller-supplied-contact-url",
  "fixture": false,
  "pagesProcessed": 1,
  "requestCount": 1,
  "diagnosticCounts": { "NO_PUBLIC_PHONE": 1 },
  "completedAt": "2026-09-08T00:00:00.000Z"
}
```

`SUCCEEDED` means at least one normal record was produced. `COMPLETED_WITH_DIAGNOSTICS` means normal records and diagnostics coexist. `NO_PUBLIC_DATA`, `BLOCKED`, or `INVALID_INPUT` never count as a successful phone extraction.

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

### Cost and troubleshooting

Cost depends on the number of supplied pages, response sizes, retries, redirects, proxy use, and Actor runtime. Start with one URL, `maxItems` 1–5, a bounded timeout, and `maxRequestRetries: 0`. Apify compute, proxy, and storage pricing is shown in the Console for your account and plan; this README does not invent a fixed price.

If a run contains only a diagnostic, inspect `errorCode`, `error`, the fresh build/run IDs, the dataset, and `OUTPUT_SUMMARY`. `NO_PUBLIC_PHONE` means the bounded public response was usable but did not expose a qualifying retailer-business number. `BLOCKED` or `LOGIN_REDIRECT` means the requested public route was not available to this client. Do not add credentials or replace the source with a directory, reader, cache, or generic retailer lookup.

For an issue report, include the current build ID, run ID, input route, proxy setting, diagnostic code, and whether fixture mode was enabled. Use the Actor Issues tab or API support request.

### Privacy, legal, and non-affiliation

This is an independent public-data extraction tool and is not affiliated with, endorsed by, or sponsored by Instacart. Instacart is a trademark of its respective owner.

The Actor is designed to collect only retailer-business contact information that the caller-supplied public page explicitly publishes. It does not intentionally collect private customer data, credentials, cookies, authenticated sessions, protected endpoints, or personal contact information from unrelated pages. Public business pages can still contain personal data. You are responsible for having a lawful and legitimate purpose, complying with Instacart’s terms and robots guidance, respecting applicable privacy and data-protection laws, securing downloaded data, and honoring requests or restrictions that apply to your use. Do not use this Actor to evade access controls or to identify a person from a phone number.

### Local development

From this Actor directory:

```bash
npm test
npm run lint
npm run schema
apify run --purge --input-file test/inputs/live-smoke.json
npm run validate
```

Only non-fixture normal rows from a fresh, exact current Cloud build can qualify the Actor. For list-like inputs, qualification also requires at least two distinct target-backed normal rows; diagnostics and fixture rows do not count.

# Changelog

This Actor's version history is a separate document: https://apify.com/w3crawler/instacart-phone-number-scraper/changelog.md

# Actor input Schema

## `contactUrls` (type: `array`):

One or more public HTTPS Instacart retailer or location contact pages supplied by the caller. Use a page that explicitly publishes the retailer-business phone; no default page is treated as evidence.

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

Compatibility alias for contactUrls. Public HTTPS Instacart retailer or storefront pages only; product pages are ignored for phone extraction.

## `urls` (type: `array`):

Compatibility alias for additional caller-supplied contactUrls. Duplicate URLs are removed before fetching.

## `url` (type: `string`):

Compatibility alias for one additional caller-supplied public HTTPS Instacart retailer or storefront page.

## `maxItems` (type: `integer`):

Maximum normal retailer phone records emitted in one run. Diagnostics do not consume this limit.

## `timeoutMs` (type: `integer`):

Maximum time allowed for each direct request.

## `maxBytes` (type: `integer`):

Maximum response size retained from a public page.

## `requestDelayMs` (type: `integer`):

Bounded respectful delay used before a retry or redirect; the first request is sent immediately.

## `maxRequestRetries` (type: `integer`):

Retries for timeouts, transport failures, and HTTP 5xx responses only; access blocks are not bypassed.

## `userAgent` (type: `string`):

Optional transparent user-agent string. Credentials and control characters are rejected.

## `proxyConfiguration` (type: `object`):

Optional Apify Proxy configuration. Direct access is the default; an empty group list uses the ordinary proxy pool when enabled. Proxy use does not bypass access controls.

## `fixture` (type: `boolean`):

Local QA option using the checked-in HTML fixture. Keep false for live public requests.

## Actor input object example

```json
{
  "contactUrls": [],
  "startUrls": [],
  "urls": [],
  "url": "",
  "maxItems": 50,
  "timeoutMs": 30000,
  "maxBytes": 2000000,
  "requestDelayMs": 0,
  "maxRequestRetries": 1,
  "userAgent": "Mozilla/5.0 (compatible; ApifyActor/1.0; +https://apify.com/)",
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "fixture": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `runSummary` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("w3crawler/instacart-phone-number-scraper").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("w3crawler/instacart-phone-number-scraper").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 '{}' |
apify call w3crawler/instacart-phone-number-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,w3crawler/instacart-phone-number-scraper"
        }
    }
}
```

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/i8kYbQbk29ccjaNHF/builds/CpXfbhmSgdJG6Ngab/openapi.json
