# Migrate from BigPicture to SavvyIQ

Move your BigPicture API integration to SavvyIQ. Endpoint mapping, auth changes, response shapes, and side-by-side code samples.

## Why migrate

- **More accurate business data.** BigPicture matched by domain, so company size, revenue, and industry frequently came back wrong (e.g., Google has thousands of domains; small businesses on shared hosting hit the same problem). SavvyIQ resolves to the legal entity first, then attaches data.
- **Real industry classification.** NAICS and SIC codes with per-code confidence, not a flat tag list. The most-requested BigPicture feature.
- **Ownership.** Ultimate-parent research with evidence. Not available in BigPicture.
- **Stable entity IDs.** One record per business, queried by ID instead of by URL.
- **Same throughput.** 600 requests/minute.

## Pricing and migration support

- **Pricing.** Usage-based, published on [savvyiq.ai/pricing](https://savvyiq.ai/pricing). [Contact sales](mailto:sales@savvyiq.ai) for volume.
- **Migration credits.** Free credits available to cover testing and any data-parity issues during cutover. Ask support.
- **Parallel run supported.** Both APIs accept traffic today. Run them side by side during cutover and compare responses.
- **Time to migrate.** Most integrations complete the port in under an hour.

## What changes

- **Base URL.** `company.bigpicture.io` → `api.savvyiq.ai`
- **Auth header.** `Authorization: keyId:keySecret` → `apikey: TOKEN` (lowercase)
- **Flow.** 1 call → 2 calls (resolve, then fetch the full record by `siq_` ID)
- **Webhooks.** Per-call `webhook_url`, like BigPicture's `webhookUrl`, plus `custom_id` for correlation. See [Webhooks](/docs/guides/webhooks).
- **Response shape.** Different field paths, mapped below.

## Endpoint mapping

| BigPicture | SavvyIQ |
| --- | --- |
| `GET /v1/companies/find` | `GET /v3/domain-intelligence` |
| `GET /v1/companies/find/stream` | `GET /v3/domain-intelligence` |
| `GET /v2/companies/search` | `GET /v3/entity-resolution/async` + `GET /v3/runs/{run_id}` |
| `GET /v2/companies/ip` | No equivalent. [Contact us](#need-help). |

For full business data after step 1, call `GET /v3/entities/{id}`.

## Authentication

1. Sign up at [app.savvyiq.ai/signup](https://app.savvyiq.ai/signup).
2. Generate a key at [app.savvyiq.ai/api-keys](https://app.savvyiq.ai/api-keys).
3. Send it on every request:

**Before**

```bash
Authorization: a1b2c3d4...:s3cr3tValu3...
```

**After**

```bash
apikey: S6yS02QKbzUyR4Uyihut2f7qNf2bJBLZQ
```

## Step 1: resolve

### By domain

`GET /v3/domain-intelligence` answers in seconds for a domain we hold. A new domain returns `status: "building"`; poll until `complete`, or pass `webhook_url`. Returns a `siq_` entity ID for step 2.

**cURL**

```bash
curl "https://api.savvyiq.ai/v3/domain-intelligence?domain=uber.com" \
  -H "apikey: YOUR_API_KEY"
```

**JavaScript**

```javascript
async function findByDomain(domain) {
  const url = `https://api.savvyiq.ai/v3/domain-intelligence?domain=${encodeURIComponent(domain)}`;
  const headers = { apikey: process.env.SAVVYIQ_API_KEY };
  for (let i = 0; i < 60; i++) {
    const data = await (await fetch(url, { headers })).json();
    if (data.status === 'complete') return data;
    if (!['building', 'refreshing'].includes(data.status)) throw new Error(data.status);
    await new Promise(r => setTimeout(r, 10000));
  }
  throw new Error('Timed out');
}
```

**Python**

```python
import os, time, requests

def find_by_domain(domain):
    url = "https://api.savvyiq.ai/v3/domain-intelligence"
    headers = {"apikey": os.environ["SAVVYIQ_API_KEY"]}
    for _ in range(60):
        data = requests.get(url, params={"domain": domain}, headers=headers).json()
        if data.get("status") == "complete":
            return data
        if data.get("status") not in ("building", "refreshing"):
            raise RuntimeError(data.get("status"))
        time.sleep(10)
    raise RuntimeError("Timed out")
```

### By name

Submit, then poll the run. Returns a `siq_` entity ID.

**cURL**

```bash
# Submit
curl -G "https://api.savvyiq.ai/v3/entity-resolution/async" \
  --data-urlencode "name=Datadog" \
  --data-urlencode "location=New York, NY" \
  -H "apikey: YOUR_API_KEY"

# Poll (202 while running, 200 when done)
curl "https://api.savvyiq.ai/v3/runs/run_344tSxhAVACWbuENINJ9D" \
  -H "apikey: YOUR_API_KEY"
```

**JavaScript**

```javascript
async function resolveByName(name, location) {
  const headers = { apikey: process.env.SAVVYIQ_API_KEY };
  const params = new URLSearchParams({ name, location });
  const { run_id } = await (await fetch(`https://api.savvyiq.ai/v3/entity-resolution/async?${params}`, { headers })).json();
  for (let i = 0; i < 60; i++) {
    const res = await fetch(`https://api.savvyiq.ai/v3/runs/${run_id}`, { headers });
    if (res.status === 200) return res.json();
    await new Promise(r => setTimeout(r, 10000));
  }
  throw new Error('Timed out');
}
```

**Python**

```python
import os, time, requests

def resolve_by_name(name, location):
    headers = {"apikey": os.environ["SAVVYIQ_API_KEY"]}
    run_id = requests.get(
        "https://api.savvyiq.ai/v3/entity-resolution/async",
        params={"name": name, "location": location},
        headers=headers,
    ).json()["run_id"]
    for _ in range(60):
        res = requests.get(f"https://api.savvyiq.ai/v3/runs/{run_id}", headers=headers)
        if res.status_code == 200:
            return res.json()
        time.sleep(10)
    raise RuntimeError("Timed out")
```

## Step 2: get the full business record

Pass the `siq_` entity ID to `/v3/entities/{id}`. Returns names, headquarters, identifiers, industry codes, firmographics, social handles. The endpoint never blocks: a record that has not been enriched yet is served with `enrichment.status: "building"` and the build starts in the background. Poll until `complete`.

**cURL**

```bash
curl "https://api.savvyiq.ai/v3/entities/siq_33d67jj0wJESjEdFHMhuQ" \
  -H "apikey: YOUR_API_KEY"
```

**JavaScript**

```javascript
async function getEntity(entityId) {
  const url = `https://api.savvyiq.ai/v3/entities/${entityId}`;
  const headers = { apikey: process.env.SAVVYIQ_API_KEY };
  for (let i = 0; i < 60; i++) {
    const body = await (await fetch(url, { headers })).json();
    if (body.enrichment.status === 'complete') return body.entity;
    if (body.enrichment.status === 'failed') throw new Error('enrichment failed');
    await new Promise(r => setTimeout(r, 10000));
  }
  throw new Error('Timed out');
}
```

**Python**

```python
import os, time, requests

def get_entity(entity_id):
    url = f"https://api.savvyiq.ai/v3/entities/{entity_id}"
    headers = {"apikey": os.environ["SAVVYIQ_API_KEY"]}
    for _ in range(60):
        body = requests.get(url, headers=headers).json()
        if body["enrichment"]["status"] == "complete":
            return body["entity"]
        if body["enrichment"]["status"] == "failed":
            raise RuntimeError("enrichment failed")
        time.sleep(10)
    raise RuntimeError("Timed out")
```

Full schema: [Business Intelligence API](/docs/apis/business-intelligence). For large lists, submit with `custom_id` and collect by webhook or by polling out of band. See [Batch processing](/docs/guides/batch-processing).

## Response shape

The record from step 2 covers what BigPicture customers asked for most: industry classification, employee count, revenue, plus founding date, market cap, funding raised, and social handles.

| BigPicture | SavvyIQ (`GET /v3/entities/{id}`) |
| --- | --- |
| `name` | `entity.display_name` |
| `legalName` | `entity.legal_name` |
| `domain` | `entity.attributes.domain` |
| `url` | `entity.website` |
| `description` | `entity.description` |
| `foundedYear` | `entity.attributes.founding_date` (YYYY-MM-DD) |
| `geo.*` | `entity.headquarters.address.*` |
| `tags` | `entity.classification.business_tags[]` |
| `category.*` (incl. `naicsCode`) | `entity.industry.schemes.naics_2022[]`, `entity.industry.schemes.sic[]` (with per-code confidence, primary first) |
| `metrics.employees`, `metrics.employeesRange` | `entity.attributes.employee_count`, `entity.attributes.employee_range` |
| `metrics.annualRevenue`, `metrics.estimatedAnnualRevenue` | `entity.attributes.revenue`, `entity.attributes.revenue_range` |
| `metrics.marketCap` | `entity.attributes.market_cap` |
| `metrics.raised` | `entity.attributes.funding_total_usd` |
| `ticker` | `entity.attributes.stock_ticker` |
| `type` (public/private) | `entity.attributes.is_public`, `entity.facets.sector` |
| `linkedin.handle`, `facebook.handle`, `twitter.handle`, `crunchbase.handle` | `entity.social_profiles.{linkedin,facebook,twitter,crunchbase}.handle` |
| `logo`, `phone`, `tech`, `alexa*Rank` | Not returned |
| `trancoRank` | `data.domain_intelligence.tranco_rank` on `/v3/domain-intelligence` |

Fields that are unknown come back `null`, never as a guess. See the [Business Intelligence API](/docs/apis/business-intelligence) for a full example response.

## Errors

```json
// 400, 404, 500
{ "error": { "message": "...", "type": "bad_request" } }

// 401
{ "message": "No API key found in request", "request_id": "..." }

// 402, 403, 429 (plan limits)
{ "error": "insufficient_credits", "message": "..." }
```

| Status | Meaning |
| --- | --- |
| 400 | Bad parameter. Do not retry. |
| 401 | Bad or missing API key. |
| 402 | Account balance too low. Top up, then retry. |
| 403 | API not enabled for your account. |
| 404 | Entity not found. |
| 429 | Rate limited, or a plan limit was reached. Back off. |
| 500 | Server error. Retry with backoff. |
| 503 | Billing temporarily unavailable. Retry with backoff. |
| 504 | Gateway timeout. Retry with backoff. |

## Rate limits

600 requests/minute per account, counted per endpoint. Every API key on an account shares the same
allowance. Headers on every response:

- `RateLimit-Limit`
- `RateLimit-Remaining`
- `RateLimit-Reset`

## Parity gaps

- **Webhooks.** Supported per call: pass `webhook_url` on any `/v3` async request, the way `webhookUrl` worked. What `webhookId` did for you is `custom_id`: pass it on the request and it comes back on the delivery, on the `202`, and on every poll. See [Webhooks](/docs/guides/webhooks).
- **IP lookup.** No equivalent. [Contact us](#need-help).

## Dashboard

Keys, usage, and billing: [app.savvyiq.ai](https://app.savvyiq.ai).

## FAQ

**Will my BigPicture API key keep working?**
Yes. `company.bigpicture.io` continues to accept requests; migrate on your own schedule.

**Can I run BigPicture and SavvyIQ in parallel?**
Yes. Both APIs accept traffic today. Compare responses side by side during cutover.

**What does it cost?**
Usage-based, published on [savvyiq.ai/pricing](https://savvyiq.ai/pricing). Every account starts with free credit. [Contact sales](mailto:sales@savvyiq.ai) for volume.

**Are migration credits available?**
Yes. Email support to request credits for testing and any data-parity issues during cutover.

**How long does the migration take?**
Under an hour for most integrations. Half a day if you have many call sites or complex response parsing.

**How do I report a migration issue?**
Email support with your BigPicture account ID, the endpoint you're porting, and a sample request/response pair.

**Will the SavvyIQ API change again soon?**
No breaking changes to the endpoints in this guide are planned. Email support to be notified of future changes.

## Need help

Email support if anything blocks your migration.
