> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lumisreach.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Predictions

> Predict how a video, text post or image post will do on Instagram, X or LinkedIn before it goes out, scored against your own account's history.

These endpoints run the models behind **Dashboard → Predicted performance** on content made anywhere.
Send a reel, a text post or a picture post, where it will be posted, and the account's own past posts;
you get back one overall score, how it should rank among that account's posts, the views, likes and
comments to expect, and what in the content moved the prediction.

| Endpoint | What it does | Scope |
| - | - | - |
| `POST /v1/predictions` | Start a prediction | `predictions:write` |
| `GET /v1/predictions/{id}` | Its status and, once done, the result | `predictions:read` |
| `GET /v1/predictions` | Every prediction in the workspace, newest first | `predictions:read` |

> [!NOTE]
>
> Each workspace can run up to 5 predictions at once and 200 per rolling 24 hours, past which `POST` returns
> `429 prediction_limit`.

## Pricing

What a prediction costs depends on the plan:

| Plan | Predictions |
| - | - |
| API & MCP | Charged per prediction from your prepaid balance |
| Growth Marketing Manager (agent hire) | Charged per prediction from your prepaid balance |
| Revenue Engine, Custom | Free |
| Any other plan | Not included: `POST` returns `403 plan_not_included` |

A charged prediction costs what its model calls cost us plus 20%. That works out to a few cents:

| Prediction | Price |
| - | - |
| Text or image post, X or LinkedIn | 5¢ |
| Text or image post, X and LinkedIn | 7¢ |
| Image post, Instagram | 7¢ |
| Image post, X, LinkedIn and Instagram | 12¢ |
| Video, no past reels to compare against | 6¢ |
| Video, compared against five past reels | 9¢ |

The price is quoted in `price_cents` as soon as you send the request, and charged only once the
prediction succeeds: a failed one costs nothing, and `charged` says when it has been. If the balance
cannot cover it and auto-refill is off, `POST` returns `402 payment_required` and nothing runs. It shows
up as `ai_prediction` in `GET /v1/billing/events`.

## What can be predicted

Each format is predicted only where a model can read it:

| `format` | What it is | `platforms` |
| - | - | - |
| `video` | A reel: send `video_url` | `instagram` |
| `text` | Text alone: send `text` | `x`, `linkedin` |
| `image` | Text with pictures: send `image_urls` | `x`, `linkedin`, `instagram` |

Any other combination is refused with `400`; Instagram takes no text-only post. Media URLs must be public
`https` addresses. Pictures are downloaded once (10 MB each at most, up to 20); X and LinkedIn read the
first four, Instagram the first ten, and all are counted.

### Image posts on Instagram

An image post on Instagram is read by the reel model, the one that predicts `video`, adapted to take
pictures and text instead of a video:

* the pictures stand in for the reel's frames, the first one its opening, and `text` is its caption;
* an AI looks through the pictures in order for the same review it gives a reel;
* length, cuts, motion, speech and sound don't apply to a picture post, so they count as an average
  reel's and move nothing;
* the account's past reels are not watched next to it.

It was trained on reels only, so read `percentile` as "this post against the account's usual reel". The
counts still scale from the `instagram` history you send, which may mix reels and picture posts. Sent to
X or LinkedIn in the same request, the post is read there by the post models as usual, and the result
has one entry in `platforms` for each.

```json theme={null}
{
  "format": "image",
  "platforms": ["instagram"],
  "text": "3 ways to wear the linen shirt this spring. Which one is you? #linenstyle #springoutfits",
  "image_urls": [
    "https://cdn.example.com/linen-1.jpg",
    "https://cdn.example.com/linen-2.jpg",
    "https://cdn.example.com/linen-3.jpg"
  ],
  "accounts": { "instagram": { "followers": 12500 } }
}
```

## Start a prediction

```bash theme={null}
curl -X POST "https://api.lumisreach.com/api/v1/predictions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "image",
    "platforms": ["x", "linkedin"],
    "text": "We cut our onboarding from 9 steps to 3. Here is what we removed and why.",
    "image_urls": ["https://cdn.example.com/onboarding-before-after.png"],
    "post_at": "2026-11-03T15:00:00Z",
    "accounts": {
      "x": {
        "followers": 4200,
        "history": [
          { "text": "Shipping week recap", "posted_at": "2026-10-20T16:00:00Z", "views": 3100, "likes": 41, "comments": 6, "reposts": 4 },
          { "text": "Why we killed our free plan", "posted_at": "2026-10-13T16:00:00Z", "views": 8900, "likes": 120, "comments": 33, "reposts": 15 }
        ]
      },
      "linkedin": { "followers": 1800 }
    }
  }'
```

It runs in the background, so this answers `202` straight away:

```json theme={null}
{
  "data": {
    "id": "6f0c1e7a-2b54-4c1e-9a53-0d8f5f6a7b21",
    "status": "processing",
    "format": "image",
    "platforms": ["x", "linkedin"],
    "created_at": "2026-10-27T18:04:11.000Z",
    "completed_at": null,
    "error": null,
    "price_cents": 7,
    "charged": false,
    "result": null
  }
}
```

### The account's history

`accounts` is keyed by platform. For each one, send what you know:

* `followers`: sizes the prediction for an account with little history.
* `history`: the account's past posts there, with their `text`, `posted_at` and whatever counts you
  have (`views`, `likes`, `comments`, `reposts`). Up to 100.

The history is what makes a prediction about **your** account rather than a typical one. Posts from the
last 90 days that are at least two days old set what your usual post does, and the counts in the result
scale from it. For text and image posts the newest 20 are also read for how alike the new post is to
them and how soon after the last one it goes out. For a video, give past reels a `video_url`: the newest
five are watched next to the new one, which ranks it against your own work.

Without any history the counts come from your follower count, or from a benchmark for a new account;
`result.basis` says which.

## Poll for the result

```bash theme={null}
curl "https://api.lumisreach.com/api/v1/predictions/6f0c1e7a-2b54-4c1e-9a53-0d8f5f6a7b21" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Poll every 5 to 10 seconds. A post usually takes under a minute; a video up to six, most of it the model
watching it.

```json theme={null}
{
  "data": {
    "id": "6f0c1e7a-2b54-4c1e-9a53-0d8f5f6a7b21",
    "status": "succeeded",
    "result": {
      "overall_score": 81,
      "overall_score_parts": [
        { "label": "Rank among your posts", "chance": 0.58, "weight": 1 },
        { "label": "Quality score", "chance": 0.51, "weight": 0.5 }
      ],
      "basis": "history",
      "quality": {
        "overall": 68,
        "hook": { "score": 74, "reason": "Opens on a concrete before-and-after number." },
        "script": {
          "score": 66,
          "reason": "Promises the list but the post itself only names two of the steps."
        },
        "video": { "score": 61, "reason": "The chart reads well but the labels are small on mobile." },
        "source": "review"
      },
      "platforms": [
        {
          "platform": "x",
          "model_version": "posts-v4",
          "percentile": 0.61,
          "relative": {
            "engagement": { "low": 0.6, "expected": 1.3, "high": 2.9 },
            "likes": { "low": 0.6, "expected": 1.3, "high": 2.8 },
            "comments": { "low": 0.5, "expected": 1.4, "high": 3.2 },
            "reposts": { "low": 0.4, "expected": 1.2, "high": 3.1 },
            "views": { "low": 0.7, "expected": 1.2, "high": 2.4 }
          },
          "predicted": {
            "views": { "low": 4200, "expected": 7100, "high": 14300 },
            "likes": { "low": 48, "expected": 104, "high": 225 },
            "comments": { "low": 10, "expected": 27, "high": 62 },
            "engagement": { "low": 58, "expected": 131, "high": 287 }
          },
          "drivers": [
            { "label": "Specific numbers", "impact": 0.14, "detail": "yes" },
            { "label": "Hashtags", "impact": -0.05, "detail": "0 hashtags" }
          ],
          "history_posts": 2
        }
      ],
      "total": {
        "views": { "low": 4200, "expected": 7100, "high": 14300 },
        "likes": { "low": 48, "expected": 104, "high": 225 },
        "comments": { "low": 10, "expected": 27, "high": 62 }
      },
      "likely_comments": [
        { "author": "devtools_dana", "text": "Which step did users miss the least?", "tone": "question" }
      ],
      "tips": ["Name all six removed steps in the post itself, not only in the picture."]
    }
  }
}
```

(Shortened: a real result has every platform you asked for and more drivers, comments and tips.)

### Reading the result

* **`overall_score`**: one 0 to 100 grade, read like a school grade. **70 is a typical post for the
  account**; 85 is clearly better, 55 clearly weaker. It leads with how the content ranks among the
  account's own posts, nudged by the AI's forecast and quality score (`overall_score_parts`).
* **`platforms[].percentile`**: the share of the account's own posts this one should beat (0.5 is
  typical). Treat it as a lean, not a measurement: picking the better of two of an account's posts, the
  models are right about 6 times in 10.
* **`relative`** is in multiples of the account's usual post; **`predicted`** turns them into counts,
  with a low (p10), expected (p50) and high (p90) value. Social reach is heavy-tailed, so the range is wide.
* **`drivers`**: what in the content moved expected engagement, largest first. `0.14` is +14%.
* **`quality`**: 0 to 100 scores where 50 is a routine post. For a video an AI watched it, and for an
  image post sent only to Instagram an AI looked through its pictures (`watch`); for any other post an
  AI read it (`review`). Null if that step failed; the overall score then rests on the rank.
* **`likely_comments`** are simulated, and **`tips`** are concrete changes that would raise reach.

## Failures

A prediction that could not finish ends as `failed` with the reason in `error`, for example a picture
that could not be downloaded or the model taking too long. Fix what it names and send it again.
`POST` itself returns `503 service_unavailable` when a model the request needs is down: the reel model
for a video or an Instagram image post, the post models for X and LinkedIn.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.