# Examples and recipes

export const whoamiCurl = `curl https://api.afterlib.com/v1/whoami \\
  -H "Authorization: Bearer $AFTERLIB_API_KEY"`;

export const whoamiJs = `const response = await fetch('https://api.afterlib.com/v1/whoami', {
  headers: {Authorization: \`Bearer \${process.env.AFTERLIB_API_KEY}\`},
});

const identity = await response.json();`;

export const whoamiTs = `type WhoamiRequest = Record<string, never>;

const request: WhoamiRequest = {};

const response = await fetch('https://api.afterlib.com/v1/whoami', {
  headers: {Authorization: 'Bearer ' + process.env.AFTERLIB_API_KEY},
});

const identity = await response.json();`;

export const whoamiPhp = `$response = file_get_contents('https://api.afterlib.com/v1/whoami', false, stream_context_create([
  'http' => [
    'header' => 'Authorization: Bearer ' . getenv('AFTERLIB_API_KEY'),
  ],
]));

$identity = json_decode($response, true);`;

export const whoamiPython = `import json
import os
import urllib.request

request = urllib.request.Request(
    'https://api.afterlib.com/v1/whoami',
    headers={'Authorization': f"Bearer {os.environ['AFTERLIB_API_KEY']}"},
)

with urllib.request.urlopen(request) as response:
    identity = json.loads(response.read().decode('utf-8'))`;

export const adsCurl = `curl https://api.afterlib.com/v1/ads/search \\
  -H "Authorization: Bearer $AFTERLIB_API_KEY" \\
  -H "Content-Type: application/json" \\
  -d '{
    "search": "running shoes",
    "limit": 10
  }'`;

export const adsJs = `const apiKey = process.env.AFTERLIB_API_KEY;

if (!apiKey) {
  throw new Error('AFTERLIB_API_KEY is required');
}

const response = await fetch('https://api.afterlib.com/v1/ads/search', {
  method: 'POST',
  headers: {
    Authorization: \`Bearer \${apiKey}\`,
    'Content-Type': 'application/json',
    'X-Afterlib-Client': 'example-fetch/1.0',
  },
  body: JSON.stringify({
    search: 'running shoes',
    limit: 10,
  }),
});

const body = await response.json();

if (!response.ok) {
  console.error(body.error?.requestId, body.error?.code, body.error?.message);
  throw new Error('AfterLib request failed');
}

console.log(body.items);`;

export const adsTs = `type AdsSearchRequest = {
  search?: string;
  limit?: number;
};

const apiKey = process.env.AFTERLIB_API_KEY;

if (!apiKey) {
  throw new Error('AFTERLIB_API_KEY is required');
}

const request: AdsSearchRequest = {
  search: 'running shoes',
  limit: 10,
};

const response = await fetch('https://api.afterlib.com/v1/ads/search', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + apiKey,
    'Content-Type': 'application/json',
    'X-Afterlib-Client': 'example-fetch/1.0',
  },
  body: JSON.stringify(request),
});

const body = await response.json();

if (!response.ok) {
  console.error(body.error?.requestId, body.error?.code, body.error?.message);
  throw new Error('AfterLib request failed');
}

console.log(body.items);`;

export const adsPhp = `$apiKey = getenv('AFTERLIB_API_KEY');

$response = file_get_contents('https://api.afterlib.com/v1/ads/search', false, stream_context_create([
  'http' => [
    'method' => 'POST',
    'header' => [
      'Authorization: Bearer ' . $apiKey,
      'Content-Type: application/json',
      'X-Afterlib-Client: example-php/1.0',
    ],
    'content' => json_encode([
      'search' => 'running shoes',
      'limit' => 10,
    ]),
    'ignore_errors' => true,
  ],
]));

$body = json_decode($response, true);

if (isset($body['error'])) {
  error_log($body['error']['requestId'] . ' ' . $body['error']['code']);
  throw new RuntimeException('AfterLib request failed');
}`;

export const adsPython = `import json
import os
import urllib.error
import urllib.request

api_key = os.environ["AFTERLIB_API_KEY"]

request = urllib.request.Request(
    "https://api.afterlib.com/v1/ads/search",
    data=json.dumps({"search": "running shoes", "limit": 10}).encode("utf-8"),
    method="POST",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
        "X-Afterlib-Client": "example-python/1.0",
    },
)

try:
    with urllib.request.urlopen(request) as response:
        body = json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as error:
    body = json.loads(error.read().decode("utf-8"))
    response_error = body.get("error", {})
    print(response_error.get("requestId"), response_error.get("code"), response_error.get("message"))
    raise

print(body["items"])`;

export const websiteCurl = `curl "https://api.afterlib.com/v1/websites/links?limit=10&search=product" \\
  -H "Authorization: Bearer $AFTERLIB_API_KEY"`;

export const websiteJs = `const response = await fetch('https://api.afterlib.com/v1/websites/links?limit=10&search=product', {
  headers: {Authorization: \`Bearer \${process.env.AFTERLIB_API_KEY}\`},
});

const links = await response.json();`;

export const websiteTs = `type WebsiteLinksRequest = {
  limit?: number;
  search?: string;
};

const request: WebsiteLinksRequest = {
  limit: 10,
  search: 'product',
};

const url = new URL('https://api.afterlib.com/v1/websites/links');
url.search = new URLSearchParams(
  Object.entries(request).map(([key, value]) => [key, String(value)]),
).toString();

const response = await fetch(url, {
  headers: {Authorization: 'Bearer ' + process.env.AFTERLIB_API_KEY},
});

const links = await response.json();`;

export const websitePhp = `$response = file_get_contents('https://api.afterlib.com/v1/websites/links?limit=10&search=product', false, stream_context_create([
  'http' => [
    'header' => 'Authorization: Bearer ' . getenv('AFTERLIB_API_KEY'),
  ],
]));

$links = json_decode($response, true);`;

export const websitePython = `import json
import os
import urllib.request

request = urllib.request.Request(
    'https://api.afterlib.com/v1/websites/links?limit=10&search=product',
    headers={'Authorization': f"Bearer {os.environ['AFTERLIB_API_KEY']}"},
)

with urllib.request.urlopen(request) as response:
    links = json.loads(response.read().decode('utf-8'))`;

These examples use plain HTTP so they work in any environment that can send HTTPS requests.

Set the key once:

```bash
export AFTERLIB_API_KEY="al_live_replace_me"
```

## Check account identity

Start with `GET /v1/whoami` to confirm which account the key or OAuth connection resolves to.

<ApiExampleTabs curl={whoamiCurl} javascript={whoamiJs} typescript={whoamiTs} php={whoamiPhp} python={whoamiPython} />

Response:

```json
{
	"authenticated": true,
	"authSource": "api_key",
	"user": {
		"userId": "00000000-0000-7001-8001-aa0000000001",
		"billingUserId": "00000000-0000-7001-8001-aa0000000001",
		"name": "Ada Lovelace",
		"emailAddress": "ada@example.com",
		"planId": "pro",
		"isTeamMember": false,
		"subscriptionStatus": "active"
	},
	"apiKey": {
		"id": "019e6387-e2e9-7478-9d6d-170158c523b0",
		"name": "Claude Desktop",
		"keyPrefix": "al_live_abc12345"
	},
	"oauthClientId": null
}
```

## Search ads with error handling

Use `fetch`, PHP streams, Python standard library, or curl. The JavaScript, TypeScript, PHP, and Python examples include simple error handling that preserves the public request ID.

<ApiExampleTabs curl={adsCurl} javascript={adsJs} typescript={adsTs} php={adsPhp} python={adsPython} />

## Check credits before search

Use `GET /v1/credits` before a workflow that may return many ads or pages:

```bash
curl https://api.afterlib.com/v1/credits \
  -H "Authorization: Bearer $AFTERLIB_API_KEY"
```

If the remaining balance is low, lower `limit`, narrow the search query, or wait for the next billing period.

## Search Website Library without credits

Website Library reads are no-credit in this phase.

<ApiExampleTabs curl={websiteCurl} javascript={websiteJs} typescript={websiteTs} php={websitePhp} python={websitePython} />

## Handle credit and limit errors

Treat both `402` and `429` as stop signals for the current workflow. Do not immediately retry the same credit-consuming request.

```ts
if (response.status === 402 || response.status === 429) {
	const body = await response.json();
	console.error('AfterLib credits or limits blocked the request', {
		code: body.error?.code,
		requestId: body.error?.requestId,
	});
}
```

`402` means the account needs an active subscription or available credits. `429` means a usage limit has been reached.

## Debug a failed request

1. Save `error.requestId` from the error response or the `X-Afterlib-Request-Id` response header.
2. Open the Developer Portal request log.
3. Compare the method, path, status, operation, credit charge, and key prefix.
4. Include the request ID when asking support to investigate.