> ## Documentation Index
> Fetch the complete documentation index at: https://docs.influship.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add an Instagram Creator

> Add a public Instagram creator and retrieve their profile for discovery and campaign research.

Add a public Instagram creator by handle, then retrieve their profile as enrichment progresses. Use this when profile lookup does not find a creator you want to research.

| Before you start | Details |
| - | - |
| Auth | API key or OAuth |
| Resource | Instagram handle |
| Cost | 5 credits for a new accepted ingest; reads cost 0.1 credits when successful |
| Limits | 50 accepted ingests per account per UTC day by default |

Submit Instagram handles to this endpoint.

```bash theme={null}
curl --fail-with-body --silent --show-error "https://api.influship.com/v1/creators/ingest" \
  -H "X-API-Key: $INFLUSHIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "instagram",
    "username": "fitness_coach_jane",
    "source_query": "vegan fitness coaches in Austin"
  }' -o ingest.json
```

The `source_query` field is optional. It is a free-text note for your own attribution — for example the search that surfaced this creator — and does not affect processing.

## Responses

A new ingest returns `202 Accepted`:

```json theme={null}
{
  "data": {
    "status": "ingesting",
    "creator": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "username": "fitness_coach_jane",
      "platform": "instagram"
    },
    "status_url": "/v1/creators/123e4567-e89b-12d3-a456-426614174000"
  }
}
```

For an existing creator, the API returns `200 OK` with `status: "already_exists"` and the same shape, at no charge.

## Polling for the Profile

The profile builds asynchronously. Poll the returned `status_url` (which is `GET /v1/creators/{id}`) to retrieve it:

* Profile fields and creator analysis become available at different times; inspect the timestamps below.
* While the profile is building, `GET /v1/creators/{id}` can return `404` or sparse fields. Apply an attempt limit and a deadline; do not poll indefinitely.

```bash theme={null}
STATUS_URL=$(jq -er '.data.status_url' ingest.json)
curl --fail-with-body --silent --show-error "https://api.influship.com${STATUS_URL}?include=profiles" \
  -H "X-API-Key: $INFLUSHIP_API_KEY"
```

## Poll with a budget

After a successful ingest, pass its returned `data.status_url` to this helper. It makes at most five reads, waits at least a minute between them, honors `Retry-After`, and stops at ten minutes. A populated `data_updated_at` on an expanded profile is this example's readiness criterion; it does not mean full creator analysis has finished.

```typescript poll-profile.ts theme={null}
const ingestionStatusPath = /^\/v1\/creators\/[0-9a-f-]+$/i;

function pollingDelay(retryAfter: string | null) {
  if (retryAfter === null) {
    return 60_000;
  }
  const seconds = Number(retryAfter);
  const requested = Number.isFinite(seconds) ? seconds * 1000 : Date.parse(retryAfter) - Date.now();
  return Math.max(60_000, Number.isFinite(requested) ? requested : 0);
}

export async function waitForProfile(statusUrl: string) {
  if (!ingestionStatusPath.test(statusUrl)) {
    throw new Error('Use the status_url returned by creator ingestion.');
  }
  const deadline = Date.now() + 10 * 60_000;
  for (let attempt = 0; attempt < 5; attempt++) {
    const response = await fetch(`https://api.influship.com${statusUrl}?include=profiles`, {
      headers: { 'X-API-Key': process.env.INFLUSHIP_API_KEY ?? '' },
      signal: AbortSignal.timeout(Math.min(10_000, Math.max(1, deadline - Date.now()))),
    });
    if (response.ok) {
      const record = await response.json();
      if (
        record.data.profiles?.some(
          (profile: { data_updated_at: string | null }) => profile.data_updated_at
        )
      ) {
        return record.data;
      }
    } else if (![404, 429, 500, 503].includes(response.status)) {
      throw new Error(`Profile read failed (${response.status}); stop polling.`);
    }
    if (attempt === 4) {
      break;
    }

    const delay = pollingDelay(response.headers.get('retry-after'));
    if (Date.now() + delay >= deadline) {
      break;
    }
    await new Promise((resolve) => setTimeout(resolve, delay));
  }
  throw new Error('Profile is still incomplete. Stop this job and check again later.');
}
```

Call `await waitForProfile(ingestResponse.data.status_url)` using the response from your ingest request. This bounds successful lookup charges to 0.5 credits (\$0.005), in addition to the 5-credit (\$0.05) new-ingest charge. Sparse successful reads are still lookups. The deadline is a job budget, not a promised completion time; full analysis can take longer.

Stop on a failed ingest `404` and check the handle. The helper's temporary `404` handling applies only after an accepted ingest. Preserve partial fields and [freshness timestamps](/concepts/creators-vs-profiles) while showing a processing state.

## Billing

Ingest costs **5 credits (\$0.05)** when the API accepts a new creator for enrichment (`202`). You are not charged when:

* the creator already exists (`200 already_exists`),
* the handle does not exist (`404`),
* the handle format is invalid (`422`),
* or you exceed the daily quota (`429`).

## Limits

Each account has a daily ingest quota (default 50 per UTC day). Exceeding it returns `429` with `error.code: "quota_exceeded"` and a `Retry-After` header pointing to the next UTC midnight:

```json theme={null}
{
  "error": {
    "code": "quota_exceeded",
    "message": "Daily ingest quota of 50 exceeded. Try again after it resets at UTC midnight."
  }
}
```

The quota is only consumed when a request is accepted (the `202` path). Handles that do not exist (`404`) or cannot be validated right now (`503`) do not use a slot. A later retry accepted as a new ingest consumes one quota slot and is billed normally.

If you need a higher quota, reach out at [elliot@influship.com](mailto:elliot@influship.com).

## Error Reference

| Status | Meaning | Charged |
| - | - | - |
| `202` | New creator accepted for enrichment | Yes |
| `200` | Creator already exists | No |
| `400` | Unsupported platform | No |
| `404` | Handle does not exist | No |
| `422` | Invalid handle format | No |
| `429` | Daily quota exceeded (`quota_exceeded`) or rate limit | No |
| `503` | Could not validate the handle right now (`service_unavailable`) — retry shortly; no quota consumed | No |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.