# BillionGraves Scraper (`crawlerbros/billiongraves-scraper`) Actor

Scrape BillionGraves.com - 71M+ crowd-sourced GPS-tagged grave photos plus Veteran, SSDI, and other genealogy record collections. Search by name across 10 record collections, or browse every record photographed in a specific cemetery.

- **URL**: https://apify.com/crawlerbros/billiongraves-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Agents, Automation, Integrations
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## BillionGraves Scraper

Scrape [BillionGraves.com](https://billiongraves.com) — the world's largest GPS-linked cemetery database, with 71M+ crowd-sourced grave photos plus U.S. Veteran Burial Records, the Social Security Death Index, and other genealogy record collections. Search by name across 10 record collections, or browse every record photographed in a specific cemetery. No login, no API key, no cookies required.

### What this actor does

- **Three modes:** `search` (name search across 10 collections), `byCemetery` (browse a full cemetery), and `searchCemeteries` (find cemeteries by name, with GPS/address/record-count details)
- **10 record collections:** GPS Headstones, Veteran Burial Records, Social Security Death Index, and more
- **Filters:** birth year range, death year range, state/region, country
- **GPS coordinates:** latitude/longitude included for photographed headstones and for cemeteries
- **Military service data:** branch, rank, conflict, and unit for veteran records
- **Empty fields are omitted** — every record only contains data BillionGraves actually returned

### Output fields

Grave records (`search` / `byCemetery` modes):

- `recordId`, `sourceUrl`, `thumbnailUrl`
- `fullName`, `givenNames`, `familyNames`, `maidenNames`
- `birthDate`, `deathDate`, `marriageDate`
- `city`, `county`, `state`, `country`
- `cemeteryId`, `cemeteryName`, `cemeteryUrl`
- `latitude`, `longitude` (GPS Headstones collection)
- `militaryBranch`, `militaryRank`, `militaryConflict`, `militaryUnit` (Veteran records)
- `collectionId`, `collectionTitle`
- `createdAt`
- `recordType`, `scrapedAt`

Cemetery records (`searchCemeteries` mode):

- `cemeteryId`, `cemeteryName`, `cemeteryUrl`
- `recordCount`, `mediaCount`, `curatorCount`
- `latitude`, `longitude`, `cemeteryRadiusMeters`
- `address`, `city`, `county`, `state`, `country`, `postalCode`
- `website`, `description`
- `recordType`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` / `byCemetery` / `searchCemeteries` |
| `familyNames` | string | `Smith` | Surname to search for (mode=search) |
| `givenNames` | string | – | First/given name to search for |
| `maidenNames` | string | – | Maiden name to search for |
| `collectionId` | string | `1` | One of 10 record collections |
| `birthYearMin` / `birthYearMax` | integer | – | Birth year range |
| `deathYearMin` / `deathYearMax` | integer | – | Death year range |
| `state` | string | – | State/region filter (search, searchCemeteries) |
| `country` | string | – | Country filter (search, searchCemeteries) |
| `cemeteryId` | integer | – | Cemetery ID (mode=byCemetery) |
| `cemeteryName` | string | – | Cosmetic label for output (mode=byCemetery) |
| `cemeteryNameQuery` | string | – | Cemetery name to search for (mode=searchCemeteries) — returns the `cemeteryId` you can feed into `byCemetery` |
| `maxItems` | integer | `30` | Hard cap on emitted records (1-500) |

#### Example: search for a surname in a date range

```json
{
  "mode": "search",
  "familyNames": "Johnson",
  "birthYearMin": 1850,
  "birthYearMax": 1920,
  "maxItems": 50
}
```

#### Example: search Veteran Burial Records

```json
{
  "mode": "search",
  "familyNames": "Smith",
  "collectionId": "2",
  "state": "New York",
  "maxItems": 30
}
```

#### Example: browse every record in a cemetery

```json
{
  "mode": "byCemetery",
  "cemeteryId": 107742,
  "cemeteryName": "Kaysville City Cemetery",
  "maxItems": 100
}
```

#### Example: find a cemetery's ID by name

```json
{
  "mode": "searchCemeteries",
  "cemeteryNameQuery": "Fort Sam Houston",
  "state": "Texas",
  "maxItems": 20
}
```

### Use cases

- **Genealogy research** — find ancestors' burial locations, dates, and GPS coordinates
- **Military history** — trace a veteran's service branch, rank, and conflict from their grave record
- **Cemetery documentation** — export a full photographed cemetery's roster
- **Family tree building** — cross-reference maiden names and family plots
- **Historical demographic research** — analyze birth/death year distributions by region

### FAQ

**Do I need a BillionGraves account?** No. Every mode uses BillionGraves' own public, unauthenticated search API — the same one the billiongraves.com website itself calls from your browser.

**Why do some collections have `SubscriptionMask` restrictions on the website but work here?** BillionGraves' public search endpoint returns the same free-to-view search-result snippet (name, dates, location) for every collection without requiring login — this actor surfaces exactly that public data. Full record detail pages on billiongraves.com may prompt for a subscription for some collections; this actor does not access or require that.

**Why do only some records have `latitude`/`longitude`?** GPS coordinates are captured by the BillionGraves mobile app when a volunteer photographs a headstone (Collection 1). Records sourced from indexed documents (SSDI, obituaries, historical records) don't have GPS data.

**Can I search without a surname?** Yes — `givenNames` or `maidenNames` alone also work, but a surname typically narrows results the most.

**Why does `byCemetery` mode sometimes take longer, or occasionally fail, on huge cemeteries?** Very large cemeteries (100,000+ photographed graves, e.g. major national cemeteries) can take BillionGraves' own server 30-60+ seconds to paginate, and their infrastructure occasionally times out (`504`) on these specific large aggregations regardless of client-side retries. The actor retries automatically, and this affects only a small number of exceptionally large cemeteries — the vast majority of BillionGraves' cemeteries (which have a few hundred to a few thousand records) respond in 1-3 seconds.

**How fresh is the data?** Live — every request hits BillionGraves' production API directly, so results reflect the database at the moment the actor runs.

**Why does `sourceUrl`/`thumbnailUrl` occasionally 404/410 for a record?** BillionGraves' search index is a snapshot that isn't always in perfect sync with the live site — a small fraction of older, crowd-sourced photo records (roughly 1 in 5 for some very common-name searches, in our testing) have since been deleted or unpublished by the volunteer who uploaded them, but the search index entry remains. There's no field in BillionGraves' own API response that reliably predicts this in advance, so the actor can't filter these out before emitting the record — the name/date/location/GPS fields for these records are still accurate at the time of the search, only the individual detail page or photo may since be gone.

**Why did my search return fewer records than `maxItems`?** BillionGraves' own search ranks by relevance rather than filtering strictly — for a common surname, only the first page or two of results are strong matches, after which the underlying API broadens into loosely-related records. This actor strictly re-checks every name/year/location filter you set before emitting a record, and stops early once several consecutive pages produce no further matches, rather than emitting weakly-related records just to hit `maxItems`. Try a more distinctive name/maiden-name combination, or a different `collectionId`, to pull a larger result set.

# Actor input Schema

## `mode` (type: `string`):

What to fetch.

## `familyNames` (type: `string`):

Surname to search for, e.g. `Smith`.

## `givenNames` (type: `string`):

First/given name to search for, e.g. `John`.

## `maidenNames` (type: `string`):

Maiden name to search for.

## `collectionId` (type: `string`):

Which BillionGraves-hosted collection to search.

## `birthYearMin` (type: `integer`):

Drop records with a known birth year before this.

## `birthYearMax` (type: `integer`):

Drop records with a known birth year after this.

## `deathYearMin` (type: `integer`):

Drop records with a known death year before this.

## `deathYearMax` (type: `integer`):

Drop records with a known death year after this.

## `state` (type: `string`):

Restrict to graves (or cemeteries) in this state/province/region, e.g. `Texas`, `Ontario`, `Scotland`.

## `country` (type: `string`):

Restrict to graves (or cemeteries) in this country, e.g. `United States`, `United Kingdom`, `Canada`.

## `cemeteryId` (type: `integer`):

Numeric BillionGraves cemetery ID (from a prior search's `cemeteryId` field).

## `cemeteryNameQuery` (type: `string`):

Find cemeteries by name, e.g. `Fort Sam Houston`, `Smith Cemetery`. Returns cemetery details including the numeric `cemeteryId` you can then feed into `mode=byCemetery`.

## `cemeteryName` (type: `string`):

Cemetery name to attach to output records and build a nicer `cemeteryUrl` (from a prior search's `cemeteryName` field). Purely cosmetic — not required.

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

Hard cap on emitted records. Note: very large cemeteries (100,000+ photographed graves) can take longer per page in `byCemetery` mode.

## Actor input object example

```json
{
  "mode": "search",
  "familyNames": "Smith",
  "collectionId": "1",
  "maxItems": 30
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset containing all scraped BillionGraves records.

# 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 = {
    "mode": "search",
    "familyNames": "Smith",
    "collectionId": "1",
    "maxItems": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/billiongraves-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 = {
    "mode": "search",
    "familyNames": "Smith",
    "collectionId": "1",
    "maxItems": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/billiongraves-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 '{
  "mode": "search",
  "familyNames": "Smith",
  "collectionId": "1",
  "maxItems": 30
}' |
apify call crawlerbros/billiongraves-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/billiongraves-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/ZVaISOKhFT95TEWPY/builds/NYXj1oY332gcuIbh0/openapi.json
