# Local Pack Listing Snapshot (`junipr/local-pack-listing-snapshot`) Actor

Extract visible local-pack/map-pack business entries, rankings, categories, ratings, reviews, addresses/service areas, and linked URLs from search...

- **URL**: https://apify.com/junipr/local-pack-listing-snapshot.md
- **Developed by:** [junipr](https://apify.com/junipr) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 local pack snapshot scanneds

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

## Local Pack Listing Snapshot

Extract visible businesses from supplied local-pack or map-pack result snapshots. The actor preserves rank, business name, category, rating, review count, location text, phone, website, directions link, and an optional expected-business match in a stable dataset schema.

### What you get

Each visible listing becomes one dataset row with:

- query and search location;
- local-pack position;
- business name and category;
- numeric rating and review count when visible;
- address or service-area text;
- visible phone number;
- website and directions URLs;
- optional boolean match against `expectedBusinesses`;
- source URL, issue code, evidence excerpt, scan time, and recommendation.

When `includeReport` is enabled, the default key-value store receives a Markdown summary, CSV export, issue JSON, evidence JSON, and run metadata.

### Input choices

Use `serpHtmlInputs` or `htmlInputs` to submit captured local-result HTML with query and location context. Supported cards use a local-result class or a `data-pack-position` marker. Visible headings, fields, links, rating text, and review counts are extracted without guessing absent values.

You may also provide explicit public `startUrls`. Network requests are limited to provided URLs, restricted to public HTTP(S) destinations, and optionally constrained by `allowedDomains`. Links inside listings are extracted as data but are not followed.

Extraction controls:

- `localQueries`: query context for supplied snapshots.
- `locations`: location labels used when an input object does not contain one.
- `expectedBusinesses`: names to compare with extracted business names.
- `compareExpectedBusinesses`: enable exact normalized name comparison.
- `extractRatings`: include or suppress rating and review-count fields.
- `extractBusinessUrls`: include or suppress website URLs.
- `maxPackResults` and `maxListingsPerSnapshot`: bound listings per snapshot.
- `maxQueries`, `maxLocations`, `maxSnapshots`, `maxBrowserPages`: bound the overall run.
- `includeEvidence` and `includeRawSnapshots`: control excerpts and capped source storage.
- `maxChargeUsd`: stop before a paid event would exceed the run ceiling.

### Example input

```json
{
  "localQueries": ["coffee shop nashville"],
  "locations": ["Nashville, TN"],
  "serpHtmlInputs": [
    {
      "query": "coffee shop nashville",
      "searchLocation": "Nashville, TN",
      "sourceUrl": "https://example.com/serp/coffee-nashville",
      "html": "<article class=\"local-pack-listing\" data-pack-position=\"1\" data-category=\"Coffee shop\" data-rating=\"4.8\" data-review-count=\"245\"><h3>Juniper Coffee</h3><div class=\"address\">100 Main St, Nashville, TN</div><span>615-555-0100</span><a href=\"https://coffee.example/\">Website</a><a class=\"directions\" href=\"https://maps.example/coffee\">Directions</a></article>"
    }
  ],
  "expectedBusinesses": ["Juniper Coffee"],
  "compareExpectedBusinesses": true,
  "extractRatings": true,
  "extractBusinessUrls": true,
  "maxPackResults": 10,
  "maxListingsPerSnapshot": 10,
  "maxQueries": 5,
  "maxLocations": 5,
  "maxSnapshots": 10,
  "maxBrowserPages": 5,
  "includeReport": true,
  "maxChargeUsd": 50
}
```

### Example output

```json
{
  "recordType": "local_pack_listing_snapshot",
  "recordName": "Juniper Coffee",
  "issueCode": "local_pack_listing_snapshot_ok",
  "severity": "low",
  "query": "coffee shop nashville",
  "searchLocation": "Nashville, TN",
  "packPosition": 1,
  "businessName": "Juniper Coffee",
  "category": "Coffee shop",
  "ratingValue": 4.8,
  "reviewCount": 245,
  "addressOrArea": "100 Main St, Nashville, TN",
  "phoneVisible": "615-555-0100",
  "websiteUrl": "https://coffee.example/",
  "directionsUrl": "https://maps.example/coffee",
  "expectedBusinessMatch": true,
  "sourceUrl": "https://example.com/serp/coffee-nashville",
  "scannedAt": "2026-07-10T12:00:00.000Z"
}
```

### Extraction semantics

Pack position comes from an explicit data attribute when present and otherwise follows visible card order. Ratings are returned as numbers from 0 to 5. Review counts are normalized to numbers by removing thousands separators. Address and service-area fields remain strings because local listings may show either format.

Website and directions links are resolved against the supplied source URL. A directions or map link is kept separate from the business website. Phone numbers are returned only when a supported number pattern is visible in the card.

Expected-business matching compares normalized visible names. It does not use fuzzy identity resolution. A `false` result means the extracted name did not exactly match the supplied set; `null` means matching was disabled or no expected names were provided.

Missing HTML, unsupported layouts, and access-denied or challenge content generate nonempty diagnostic rows. The actor does not create businesses, ratings, reviews, addresses, or links that are absent from the submitted evidence.

### Pay-per-event pricing

Platform usage pass-through is off. Paid events are charged before their associated output is written.

| Event | Price |
| --- | ---: |
| `actor-start` | $0.0800 |
| `local-pack-snapshot-scanned` | $0.0100 |
| `live-serp-page-rendered` | $0.0275 |
| `local-pack-record-extracted` | $0.0095 |
| `report-generated` | $0.1800 |

The default maximum charge is $50. Use a lower `maxChargeUsd` for smaller workloads; zero prevents paid processing and output.

### Practical uses

- capture local-pack visibility for a business and named competitors;
- compare visible rank, rating, and review-count changes over time;
- export business URLs, directions links, phones, and location text;
- check exact expected-business presence across city-query combinations;
- create a bounded local-search evidence table for agencies or multi-location brands.

### Limits

Search-result HTML changes across providers and regions. Structured, consistently captured card markup gives the most repeatable results. The actor reports only the supplied snapshot; it does not resolve business identities, estimate foot traffic, validate ratings, or infer missing locations.

# Actor input Schema

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

Optional explicit public URLs; private and non-HTTP targets are rejected.

## `sitemapUrls` (type: `array`):

Optional permitted public sitemap URLs.

## `htmlInputs` (type: `array`):

Deterministic HTML objects with sourceId, sourceUrl, and query context.

## `allowedDomains` (type: `array`):

Optional hostname allowlist for network fetches.

## `maxPages` (type: `integer`):

Overall HTML target cap.

## `maxDepth` (type: `integer`):

Fixed at zero because discovered links are not crawled.

## `includeEvidence` (type: `boolean`):

Include source evidence excerpts in dataset rows.

## `includeRawSnapshots` (type: `boolean`):

Store capped raw HTML in key-value storage.

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

Delay between permitted public fetches.

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

Per-request timeout in milliseconds.

## `userAgentMode` (type: `string`):

Standard or compatibility request header.

## `localQueries` (type: `array`):

Queries associated with supplied local-pack snapshots.

## `locations` (type: `array`):

Location labels associated by snapshot index.

## `serpHtmlInputs` (type: `array`):

Captured query-linked local-pack HTML objects.

## `expectedBusinesses` (type: `array`):

Optional business names or objects used for explicit match checks.

## `maxPackResults` (type: `integer`):

Compatibility cap used when maxListingsPerSnapshot is omitted.

## `extractRatings` (type: `boolean`):

Preserve visible rating and review-count evidence.

## `extractBusinessUrls` (type: `boolean`):

Preserve visible website links.

## `compareExpectedBusinesses` (type: `boolean`):

Compare names only when expectedBusinesses is nonempty.

## `maxQueries` (type: `integer`):

Maximum local queries processed.

## `maxLocations` (type: `integer`):

Maximum location labels processed.

## `maxSnapshots` (type: `integer`):

Maximum supplied snapshots processed.

## `maxListingsPerSnapshot` (type: `integer`):

Maximum local-pack rows emitted per snapshot.

## `maxBrowserPages` (type: `integer`):

Maximum explicitly supplied public snapshot URLs fetched.

## `maxChargeUsd` (type: `number`):

Maximum accepted PPE spend before paid output stops.

## `includeReport` (type: `boolean`):

Write Markdown, CSV, JSON evidence, and run metadata.

## Actor input object example

```json
{
  "startUrls": [],
  "sitemapUrls": [],
  "htmlInputs": [],
  "allowedDomains": [],
  "maxPages": 1,
  "maxDepth": 0,
  "includeEvidence": true,
  "includeRawSnapshots": false,
  "requestDelayMs": 250,
  "timeoutMs": 15000,
  "userAgentMode": "standard",
  "localQueries": [
    "coffee shop nashville"
  ],
  "locations": [
    "Nashville, TN"
  ],
  "serpHtmlInputs": [],
  "expectedBusinesses": [],
  "maxPackResults": 10,
  "extractRatings": true,
  "extractBusinessUrls": true,
  "compareExpectedBusinesses": false,
  "maxQueries": 1,
  "maxLocations": 1,
  "maxSnapshots": 1,
  "maxListingsPerSnapshot": 10,
  "maxBrowserPages": 1,
  "maxChargeUsd": 50,
  "includeReport": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `report` (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("junipr/local-pack-listing-snapshot").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("junipr/local-pack-listing-snapshot").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 junipr/local-pack-listing-snapshot --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,junipr/local-pack-listing-snapshot"
        }
    }
}

```

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/XPQUQ8u142JKd81cb/builds/GfGezobrujMPqLJqr/openapi.json
