# Profile Generator API

Realistic profiles of people who do not exist: full name, gender, age, country and a natural phone photo. JSON over HTTPS. Within one account a full name and a photo are never repeated.

Base URL: https://randomizer.beydemir.dev

## Authentication

Send the API key on every request: `Authorization: Bearer rk_...`. Create keys at https://randomizer.beydemir.dev/portal (shown once). Keys belong on servers: a key in the URL is refused (`key_in_url`) and there is no CORS.

## GET /v1/profiles

Returns up to 10 profiles per call and charges 1 credit per profile returned.

Query parameters (unknown, repeated or overlong parameters are refused with 400):

- `count`: 1 to 10, default 1
- `country`: two-letter code (see countries), random when omitted
- `gender`: `male` or `female`, random when omitted
- `minAge`, `maxAge`: 18 to 80, default 20 and 60
- `style`: `candid` (natural phone photo) or `portrait` (headshot)
- `fallback`: `false` never switches style; by default a candid request falls back to a portrait when no candid photo matches

Example:

```
curl -H "Authorization: Bearer $RANDOMIZER_KEY" "https://randomizer.beydemir.dev/v1/profiles?country=DE&gender=female&count=3"
```

Response:

```json
{
  "data": [
    {
      "id": "643b2e8c-d04b-432a-b167-b39d004ec1e7",
      "firstName": "Rania",
      "lastName": "Erhardt",
      "fullName": "Rania Erhardt",
      "gender": "female",
      "country": "DE",
      "age": 40,
      "photo": {
        "url": "https://randomizer.beydemir.dev/photos/643b2e8c-d04b-432a-b167-b39d004ec1e7",
        "style": "candid",
        "width": 640,
        "height": 800,
        "focus": { "x": 0.524, "y": 0.451 }
      }
    }
  ],
  "meta": { "requested": 1, "returned": 1 }
}
```

`meta.returned` is lower than `count` when the account already has every matching photo; `meta.incomplete` gives the reason and only returned profiles are charged. `photo.focus` is the face centre (0 to 1) for cropping.

## GET /photos/{id}

Public, no key. AVIF, 640x800 (candid) or 640x640 (portrait), cached for a year. `?w=160|320|480` gives a smaller WebP, `?format=jpg` a JPEG.

## GET /v1/countries

Supported countries: DE Germany, AT Austria, CH Switzerland, FR France, NL Netherlands, BE Belgium, GB United Kingdom, IE Ireland, PL Poland, CZ Czechia, SK Slovakia, HU Hungary, RO Romania, RS Serbia, HR Croatia, RU Russia, UA Ukraine, SE Sweden, NO Norway, DK Denmark, FI Finland, IT Italy, ES Spain, PT Portugal, GR Greece, US United States, CA Canada, AU Australia, IL Israel, BR Brazil, MX Mexico, TR Türkiye, AZ Azerbaijan, IR Iran, SA Saudi Arabia, AE United Arab Emirates, EG Egypt, IN India, CN China, TW Taiwan, JP Japan, KR South Korea, VN Vietnam, TH Thailand, ID Indonesia, NG Nigeria, ZA South Africa.

## GET /v1/usage

Balance, limits and profiles per day for the last 30 days. Free of charge.

```json
{ "account": { "id": "acc_...", "plan": "free" }, "credits": 2840, "unlimited": false,
  "limits": { "perMinute": 60, "perDay": 1000 },
  "today": { "used": 160, "remaining": 840, "resetsIn": 30512 }, "last30Days": [{ "day": "2026-10-14", "n": 160 }] }
```

## Credits and limits

1 credit = 1 profile; credits are shared with the browser generator and never expire. Buy them at https://randomizer.beydemir.dev/portal. A request the balance cannot fully cover is refused with 402 and charges nothing. Accounts may request 60 profiles a minute and 1,000 a day (UTC).

Response headers: `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (seconds), `X-Daily-Limit`, `X-Daily-Remaining`, `X-Credits-Remaining`, `X-Request-Id`. On 429, wait `Retry-After` seconds.

## Errors

`{ "error": { "code": "...", "message": "..." }, "requestId": "..." }`. Branch on `code`; messages may change.

- 400 `invalid_parameter`, `unknown_parameter`, `key_in_url`: fix the request
- 401 `missing_key`, `invalid_key`, `revoked_key`: send a valid key
- 402 `insufficient_credits`: buy credits or ask for fewer profiles
- 403 `account_suspended`
- 404 `no_photo_left`: the account has every photo matching the filters; widen the age range or change style
- 409 `names_exhausted`: almost every name for this country and gender went to the account
- 429 `rate_limited`, `daily_quota_exceeded`: wait for Retry-After
- 503 `photo_unavailable`, `busy`: temporary, retry shortly
