# Get bulk job results

Read each item's result from a match prediction bulk update job, one page at a time, once the job has finished.

- Endpoint: `GET https://api.returning.ai/v1/match-predictions/bulk-jobs/{jobId}/results`
- Section: Gamification / Match predictions
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `matchPredictions` (Shown in the dashboard as "Match Prediction")
- Retries: Read-only; poll at the Retry-After interval
- Guide: hand-written
- Verified: code, 2026-09-27
- Last updated: 26 Sep 2026
- Web page: https://docs.returning.ai/api-reference/gamification-match-predictions/get-bulk-job-results

## When to use this

- Find the matches a bulk update couldn't change, and why.
- Record the new `version` of each match the job changed.

**Instead:** Use [Get bulk job status](https://docs.returning.ai/api-reference/gamification-match-predictions/get-bulk-job-status.md) instead to check whether the job has finished.

**Availability:** api.returning.ai doesn't serve this route yet: requests return an empty `404`. Ask Returning.AI before you build on it.

## Authentication

- Header: `Authorization: Bearer <API_KEY>`
- Permission: `matchPredictions`

Use a Community API key with `matchPredictions`, shown in the dashboard as Match Prediction, and keep it on your server. The key decides the community: you only see and change match predictions in that community, and you can only read jobs created with a key from that community. Personal API keys are rejected with `403`. A job from another community returns `404`, the same as an unknown ID.

## Behaviour

api.returning.ai doesn't serve this route yet: requests return an empty `404`. Ask Returning.AI before you build on it.

While the job is `queued` or `processing`, this returns `202 MATCH_PREDICTION_BULK_UPDATE_IN_PROGRESS` with progress counts and a `Retry-After` header of 3 seconds, and no items. Once it has finished, it returns `200` with the item results. A `failed` job lists only the items it reached. Jobs are kept for 7 days.

## Request

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `jobId` | `string` | Yes | The job ID, `operationId` from Bulk update match predictions. (`mpbu_` and 32 hex characters) |

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | `string` | No | Only applied items, or only failed items. Omit for both. (`updated` or `failed`) |
| `page` | `integer` | No | Page number, starting at 1. (Whole number, 1 or more; default 1) |
| `limit` | `integer` | No | Items per page. (Default 100; max 1000) |

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key with `matchPredictions`. (`Bearer <API_KEY>`) |
| `X-Request-Id` | `string` | No | Your own ID for this request, returned in `meta.requestId`. Without it, a `req_...` ID is generated. |

### Example request

```bash
curl --request GET \
  --url 'https://api.returning.ai/v1/match-predictions/bulk-jobs/mpbu_66f00000000000000000000000000802/results?status=failed' \
  --header 'Authorization: Bearer <API_KEY>'
```

## Response

A `200` returns one result per item. `meta.total` counts the items that match your `status` filter. For a `VERSION_CONFLICT`, read the match again before you retry the change.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `meta` | `object` | always | Result details. |
| `meta.status` | `string` | always | Result of the request. (`success`) |
| `meta.statusCode` | `integer` | always | The HTTP status, repeated. |
| `meta.code` | `string` | always | Machine-readable result code. (`MATCH_PREDICTION_BULK_JOB_RESULTS_RETRIEVED`) |
| `meta.requestId` | `string` | always | Your `X-Request-Id` if you sent one, otherwise a generated `req_...` ID. Quote it when you contact support. |
| `meta.page` | `integer` | always | The page you asked for. |
| `meta.limit` | `integer` | always | Items per page. |
| `meta.total` | `integer` | always | Items that match, across all pages. |
| `message` | `string` | always | Human-readable summary. Do not branch on it. |
| `data` | `object` | always | The job and this page of item results. |
| `data.jobId` | `string` | always | The job ID. |
| `data.status` | `string` | always | How the job ended. (`completed`, `completed_with_errors` or `failed`) |
| `data.items` | `object[]` | always | This page of results, in the order the items were sent. |
| `data.items.matchPredictionId` | `string` | - | The match prediction the item was for, as you sent it. |
| `data.items.status` | `string` | - | Whether the change was applied. (`updated` or `failed`) |
| `data.items.newVersion` | `integer` | - | The match's version after the change. Only on `updated` items. |
| `data.items.resourceUrl` | `string` | - | The match's path, without the host. Only on `updated` items. |
| `data.items.error` | `object` | - | Why the item failed. Only on `failed` items. |
| `data.items.error.statusCode` | `integer` | - | The status Patch match prediction would have returned. |
| `data.items.error.code` | `string` | - | The reason, as a code. The same codes as Patch match prediction. (`MATCH_PREDICTION_NOT_FOUND`, `VERSION_CONFLICT`, `LOCKED_FIELDS_IMMUTABLE`, `SCHEDULE_MAY_NOT_MOVE_EARLIER`, `MATCH_PREDICTION_VALIDATION_FAILED` or `INTERNAL_ERROR`) |
| `data.items.error.message` | `string` | - | Human-readable reason. Do not branch on it. |
| `data.items.error.details` | `object[]` | - | Each problem, for `MATCH_PREDICTION_VALIDATION_FAILED`, with `field`, `code` and `message`. |

### Example response (200)

```json
{
  "meta": {
    "status": "success",
    "statusCode": 200,
    "code": "MATCH_PREDICTION_BULK_JOB_RESULTS_RETRIEVED",
    "requestId": "req_66f00000000000000000000000000811",
    "page": 1,
    "limit": 100,
    "total": 2
  },
  "message": "Bulk job results retrieved.",
  "data": {
    "jobId": "mpbu_66f00000000000000000000000000802",
    "status": "completed_with_errors",
    "items": [
      {
        "matchPredictionId": "mp_66f000000000000000000801",
        "status": "updated",
        "newVersion": 3,
        "resourceUrl": "/v1/match-predictions/mp_66f000000000000000000801"
      },
      {
        "matchPredictionId": "mp_66f000000000000000000807",
        "status": "failed",
        "error": {
          "statusCode": 404,
          "code": "MATCH_PREDICTION_NOT_FOUND",
          "message": "Match prediction not found.",
          "details": []
        }
      }
    ]
  }
}
```

## Errors

Every error carries its code in `meta.code` and the request's ID in `meta.requestId`. Validation errors list each problem in `details`, with a `field`, `code` and `message`. The API key checks (`401`, `403`, `404 COMMUNITY_NOT_FOUND` and `500 AUTHENTICATION_FAILED`) return `detail` and `solution` instead. A `404` with an empty body means the route isn't available on api.returning.ai yet.

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 404 | - | The body is empty: api.returning.ai doesn't serve this route yet. Ask Returning.AI before you build on it. |
| 400 | `MATCH_PREDICTION_VALIDATION_FAILED` | A query value is invalid: `page` below 1, `limit` outside 1-1,000, or a `status` other than `updated` or `failed`. `details` names it. |
| 401 | `AUTHENTICATION_REQUIRED` | The key is missing, invalid or expired. Send `Authorization: Bearer <API_KEY>` with a current Community API key. |
| 403 | `API_KEY_PERMISSION_DENIED` | The key lacks `matchPredictions`, or it is a personal key. Use a Community API key and add Match Prediction in Settings > Integration > API Keys. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `MATCH_PREDICTION_BULK_JOB_NOT_FOUND` | No job in your community has this ID, or the job is more than 7 days old. Check the `operationId` from Bulk update match predictions. |
| 404 | `COMMUNITY_NOT_FOUND` | The community this key belongs to no longer exists. Contact Returning.AI support. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 429 | `RATE_LIMITED` | Your community is over its [rate limit](https://docs.returning.ai/how-it-works.md#rate-limits). Wait for the seconds in `Retry-After`, then retry. |
| 500 | `INTERNAL_ERROR` | The results could not be read. Retry with exponential backoff. |
| 500 | `AUTHENTICATION_FAILED` | The key could not be checked. Retry with backoff; nothing changed. |
| 503 | `MATCH_PREDICTION_FROZEN` | Returning.AI has paused match prediction requests. Nothing changed. Retry later with backoff. |

**Retries:** This endpoint is read-only, so repeating it is safe. While it returns `202`, wait the `Retry-After` seconds between polls. Reading results never runs the job again. Returning.AI can set a request limit for your community on these endpoints, counted per community and calling IP address. Over it, requests return `429 RATE_LIMITED` with a `Retry-After` header in seconds; wait that long, then retry.

## Next step

- [Patch match prediction](https://docs.returning.ai/api-reference/gamification-match-predictions/patch-match-prediction.md): `PATCH /v1/match-predictions/{matchPredictionId}`. Read a failed match again for its current `version`, then send the change on its own or in a new job.
