# Interpreting API responses

> Every reply from the Nyne.ai API arrives in the same envelope, so reading one correctly means reading them all. Branch on `success` first, tell a finished result from a still-running one, read confidence scores where they exist, and understand why the data is clean, merged, and never carries a vendor name.

HTML version: https://api.nyne.ai/documentation/responses

The shape is always the same: `success` (true or false), then `data` or `error` (one, never both), then `timestamp` (server UTC, ISO-8601). Branch on `success` first; on failure switch on `error.code`, not the message.

## The uniform envelope

Success or failure, from any endpoint, the response is wrapped in the same keys. On success you get `success: true` and the payload under `data`:

```json
{
  "success": true,
  "data": {
    "request_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4_1717000000_4271",
    "status": "completed",
    "completed": true,
    "result": {
      "displayname": "Jane Doe",
      "headline": "VP of Product at Acme",
      "best_business_email": "jane.doe@acme.com"
    }
  },
  "timestamp": "2026-05-28T18:04:45"
}
```

When something goes wrong, `success` is `false`, there is no `data`, and the detail sits under `error`:

```json
{
  "success": false,
  "error": {
    "code": "insufficient_credits",
    "message": "Not enough credits to complete this request."
  },
  "timestamp": "2026-05-28T18:04:45"
}
```

Always branch on `success` first, then read `error.code` for the failure branch. The `error.code` is a stable, machine-readable identifier; the human `message` is for logs and can change wording at any time, so never parse it. The `timestamp` is the server's UTC time in ISO-8601 - useful for correlating logs, not for logic.

## Async status: done or not done

Most data endpoints answer asynchronously. A `202` means the request was accepted but is **not finished**. While it runs, `data.status` reads `pending`, `queued`, or `processing`, and `data.completed` is `false`:

```json
{
  "success": true,
  "data": { "request_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4_1717000000_4271", "status": "processing", "completed": false },
  "timestamp": "2026-05-28T18:04:12"
}
```

The result is final in exactly two cases: a `200` with `status: "completed"`, or a terminal `status: "failed"`. Either way, `data.completed` flips to `true`. Treat the request as pending while the HTTP status is `202` or `data.completed` is `false`; stop only when `data.completed` is `true`, then read `data.status` to see whether it `completed` or `failed`. See [Async requests](https://api.nyne.ai/documentation/async.md) for the full lifecycle, poll cadence, and webhook callbacks.

## Confidence and scoring

Search can rank and score its matches. Opt in with `profile_scoring`, and add `probability_score` to surface a `probability` match score (0 to 1) on each result:

```bash
curl -X POST https://api.nyne.ai/person/search \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "query": "VP of Product at Acme", "profile_scoring": true, "probability_score": true }'
```

```json
{
  "success": true,
  "data": {
    "request_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4_1717000000_4271",
    "status": "completed",
    "completed": true,
    "results": [
      { "displayname": "Jane Doe", "headline": "VP of Product at Acme", "probability": 0.94 },
      { "displayname": "Jane Doe", "headline": "Analyst at Globex", "probability": 0.42 }
    ]
  },
  "timestamp": "2026-05-28T18:04:39"
}
```

Pick a confidence bar that fits your use case and threshold on `probability` rather than blindly trusting the top match - a high bar for automated writes, a lower one for human review. Be aware of the limit: `probability` is the only confidence number the API exposes. There are no per-field confidence values, so a returned field is present or it is not.

## Data provenance and sanitization

Nyne.ai draws on many underlying sources, but responses are stripped of provider and vendor identity **by design**. Any `_provider_*` key and fields like `provider`, `provider_name`, `vendor`, `vendor_name`, `source_vendor`, and `third_party` are removed recursively, at every depth of the payload. Per-vendor prefixes are stripped the same way, and internal provider statuses are normalized to the public, stable values you poll on:

| internal | you see | Meaning |
| --- | --- | --- |
| provider_pending | processing | Still working - keep polling. |
| provider_failed | failed | Terminal. The job will not complete. |

Customers buy Nyne.ai data, not a vendor passthrough, so do not build logic that looks for source attribution - it will never be there. Build against the documented fields, which stay stable even as the data behind them changes. Internal error messages and codes are likewise scrubbed to generic public text, so do not parse stack traces or internal identifiers out of a `message`.

## Partial and failed results

A match can be partial. Fields that could not be resolved simply do not appear, or come back `null` - a profile with a name and company but no phone is a normal, useful result. Do not assume any single field will be present.

When a request finds nothing at all it presents as a terminal failure: `status: "failed"` with `completed: true`. A clean no-match commonly reads as a `404` on the underlying request:

```json
{
  "success": true,
  "data": {
    "request_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4_1717000000_4271",
    "status": "failed",
    "completed": true,
    "error": "No match found for the supplied identifiers."
  },
  "timestamp": "2026-05-28T18:04:39"
}
```

Accept partial matches that clear your confidence bar. A transient `failed` or a `503` is retryable with backoff; a clean no-match (nothing exists for those identifiers) is not - retrying just burns calls. See [Rate limits](https://api.nyne.ai/documentation/rate-limits.md) for the full retryable error taxonomy.

## Report bad data

Found something wrong or stale in a result? Close the loop with `POST /person/enrichment/feedback`. It is synchronous and returns `200` right away - pass the `request_id` from the result you are correcting and a non-empty `comments` string:

```bash
curl -X POST https://api.nyne.ai/person/enrichment/feedback \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "request_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4_1717000000_4271",
    "comments": "The current company is out of date; she left Acme in early 2026."
  }'
```

## What's next

- [Async requests](https://api.nyne.ai/documentation/async.md) - the full submit, poll, and callback lifecycle behind every 202.
- [Rate limits](https://api.nyne.ai/documentation/rate-limits.md) - which errors are retryable, and how to back off cleanly.
- [Effective requests](https://api.nyne.ai/documentation/effective-requests.md) - send stronger identifiers to get fuller, higher-confidence matches.
- [Your first request](https://api.nyne.ai/documentation/first-request.md) - the end-to-end walkthrough this page's rules apply to.
