> **For coding agents and LLMs:** This is one published Social Fetch blog post (markdown export). Product docs and API orientation live in [`/llms.txt`](https://www.socialfetch.dev/llms.txt). The HTML article is at the on-site URL below.

## This page

- **On-site (HTML):** [https://www.socialfetch.dev/blog/social-data-should-be-boring](https://www.socialfetch.dev/blog/social-data-should-be-boring)
- **Markdown (.mdx) URL:** [https://www.socialfetch.dev/blog/social-data-should-be-boring.mdx](https://www.socialfetch.dev/blog/social-data-should-be-boring.mdx)
- **Blog:** [https://www.socialfetch.dev/blog](https://www.socialfetch.dev/blog)

---

# Why social media APIs should be boring

If your product touches social networks (marketing analytics, creator CRM, brand safety, AI research), you have probably watched "fetch public posts" turn into a permanent side project.

A CSS selector moves on Instagram and your ingestion job returns empty arrays. A platform renames a field inside a script tag and your TypeScript types lie for a week before anyone notices. You get paged at 2 a.m. for a parser you wrote six months ago and forgot existed.

We built Social Fetch because that maintenance work belongs in an API layer, not on your product roadmap forever. One REST surface covering 20 platforms. Stable JSON shapes. A `requestId` on every response so support can trace a call without guessing which worker ran it.

## The boring API manifesto

"Exciting" social integrations mean headless browsers, proxy rotation, and schema drift in production. Teams need the opposite: stable field names, HTTP errors you can retry on, rotatable API keys, billing that matches completed lookups.

Product teams should ship features, not babysit scrapers. One integration path beats five hacks — especially when someone asks for a second platform by Friday. If we can't debug a failed call from a request ID, the API isn't finished.

> The best social data pipeline is the one your future self does not recognize in `git blame`, because it has not needed a fix in two years.

## Smoke test before you spend credits

Before you hit a metered route, call `GET /v1/whoami`. It checks your API key and costs nothing. Same smoke test as the [Quickstart](/docs/quickstart).

```bash
curl -sS -H "x-api-key: $SOCIALFETCH_API_KEY" \
  "https://api.socialfetch.dev/v1/whoami"
```

TypeScript version. Keys go in the `x-api-key` header (server-side only, not a browser token):

```ts
const origin = process.env.SOCIALFETCH_API_ORIGIN ?? "https://api.socialfetch.dev";

const res = await fetch(`${origin}/v1/whoami`, {
	headers: {
		"x-api-key": process.env.SOCIALFETCH_API_KEY ?? "",
	},
});

if (!res.ok) {
	const err = await res.json().catch(() => ({}));
	throw new Error(`whoami failed: ${res.status} ${JSON.stringify(err)}`);
}

const body = await res.json();
console.log(body.meta?.requestId, body.data);
```

Metered routes bill differently. Credits charge when a lookup **completes**, including `not_found` and `private` outcomes that come back as HTTP 200 with a `lookupStatus` in the body. Pre-send validation errors, `lookup_failed`, and `503 temporarily_unavailable` do not bill the same way. Reconcile your ledger against `meta.creditsCharged`, not your request count. Full rules: [Credits](/docs/credits).

## One envelope for success and errors

Successful responses wrap payloads in `{ data, meta }`. `meta` carries `requestId`, `creditsCharged`, and the API version. Errors use the same shape so you always know what to paste into a support ticket.

Log `meta.requestId` on every call. We learned that the hard way; see [The Scrape Job That Never Timed Out](/blog/social-media-scraper-api-reliability#thirty-seven-jobs-that-would-not-die). Typed error bodies live in the [Errors guide](/docs/errors). Per-route credit costs are in the [API reference](/docs/api); plan volume on [Pricing](/pricing).

> **Headers matter**
>
> API keys use the `sfk_` prefix and travel in the `x-api-key` header. Details: [Quickstart — authentication](/docs/quickstart#authentication).

> **Do not ship keys to clients**
>
> Browser bundles and mobile apps are the wrong place for API secrets. Call Social Fetch from your backend, edge worker, or cron job.

## When a custom scraper still makes sense

Sometimes a one-off script is the right tool. You need one obscure page, or you are proving something over a weekend. Fine.

> **Rule of thumb**
>
> Narrow, human scope: a custom script is fine. Core product on a schedule across platforms: you want versioning, SLAs, and someone else on pager duty when upstream HTML changes.

## Where to go next

Check [Pricing](/pricing) before you commit to a ship date. Wire auth and error handling before you parallelize heavy fetches ([Quickstart](/docs/quickstart#authentication), [Errors](/docs/errors)). Skim route coverage in the [API reference](/docs/api) before you lock a database schema.

Quickstart Guide curl examples, required headers, and the whoami smoke test. /docs/quickstart
Errors &amp; Retries The error envelope, HTTP codes, and exactly when to retry. /docs/errors
Full API Reference Paths, query parameters, and generated JSON examples. /docs/api
API Pricing Credits, bulk packs, and how to model your volume. /pricing

[/docs/quickstart](/docs/quickstart)
[/docs/errors](/docs/errors)
[/docs/api](/docs/api)
[/pricing](/pricing)

Boring plumbing. Interesting products on top. That is the split we want.
