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

# Estimate a generation's cost: POST /v1/estimate

> POST /v1/estimate prices a Percify generation in credits before you run it, including per-second lip-sync and quality multipliers. It never charges.

`POST https://api.percify.io/v3/playground/v1/estimate` returns the exact number of credits a generation will cost, without running it or charging you. It takes the same `modelId` and `input` as [`POST /v1/run`](/api-reference/generations/run) and uses the same pricing, so the number matches what the run charges.

Use it for any model whose price depends on its inputs: lip-sync billed per second of audio, voice cloning billed by the length of the voice sample, and image or video models where `quality`, `resolution` or `duration` change the price.

## Body

<ParamField body="modelId" type="string" required>
  A model id from [`GET /v1/models`](/api-reference/models).
</ParamField>

<ParamField body="input" type="object" required>
  The inputs you plan to send to `/v1/run`. Required fields must be present and `enum` values must be valid. For duration-billed models, include the audio or video URL so Percify can measure it.
</ParamField>

## Response

<ResponseField name="data.modelId" type="string">
  The model that was priced.
</ResponseField>

<ResponseField name="data.credits" type="integer">
  The credits `/v1/run` will charge for this input.
</ResponseField>

<ResponseField name="data.pricingNote" type="string">
  How the model is billed, in words: for example `flat 2 credits per run`, `billed per second of audio`, or a note that names the multipliers for a resolution or quality field.
</ResponseField>

## Example

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -s -X POST https://api.percify.io/v3/playground/v1/estimate \
    -H "Authorization: Bearer $PERCIFY_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"modelId":"flux-schnell","input":{"prompt":"a lighthouse at dawn, watercolor"}}'
  ```

  ```javascript Node.js theme={"system"}
  const res = await fetch("https://api.percify.io/v3/playground/v1/estimate", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PERCIFY_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      modelId: "infinitetalk-fast",
      input: {
        image: "https://example.com/portrait.jpg",
        audio: "https://example.com/voiceover.mp3",
      },
    }),
  });
  const { data } = await res.json();
  console.log(`${data.credits} credits (${data.pricingNote})`);
  ```

  ```python Python theme={"system"}
  import os, requests

  data = requests.post(
      "https://api.percify.io/v3/playground/v1/estimate",
      headers={"Authorization": f"Bearer {os.environ['PERCIFY_API_TOKEN']}"},
      json={
          "modelId": "infinitetalk-fast",
          "input": {
              "image": "https://example.com/portrait.jpg",
              "audio": "https://example.com/voiceover.mp3",
          },
      },
  ).json()["data"]
  print(data["credits"], data["pricingNote"])
  ```
</CodeGroup>

```json 201 Response (flux-schnell) theme={"system"}
{
  "success": true,
  "data": {
    "modelId": "flux-schnell",
    "credits": 2,
    "pricingNote": "flat 2 credits per run"
  }
}
```

## How is a duration-billed model priced?

Percify downloads the start of the audio or video you pass and reads its length. If it cannot read the length, the estimate assumes 30 seconds, while `/v1/run` may refuse the same file (for example an MP4 without a readable duration). If the estimate looks like a 30-second price for a clip of a different length, check that the URL is public and the file is a standard `.mp3`, `.wav` or `.mp4`.

## How is it different from a run?

|                                            | `/v1/estimate` | `/v1/run` |
| ------------------------------------------ | -------------- | --------- |
| Charges credits                            | No             | Yes       |
| Checks your balance                        | No             | Yes       |
| Content moderation                         | No             | Yes       |
| Starts a generation                        | No             | Yes       |
| Counts toward 60 requests a minute per key | Yes            | Yes       |

A good estimate does not guarantee the run starts: the run can still stop on moderation, your balance or a key's **Monthly credit cap**.

## Errors

| Status | Message starts with         | Cause                            |
| ------ | --------------------------- | -------------------------------- |
| `400`  | `Missing required field: …` | Add the field.                   |
| `400`  | `<field> must be one of: …` | Use a listed value.              |
| `404`  | `Model '…' not found`       | Use an id from `GET /v1/models`. |

## Related

<CardGroup cols={2}>
  <Card title="Start a generation" href="/api-reference/generations/run">
    Run it once the price is right.
  </Card>

  <Card title="List models" href="/api-reference/models">
    Each model's pricing type and inputs.
  </Card>

  <Card title="Check credits and usage" href="/api-reference/user/overview">
    Balance, monthly spend and key caps.
  </Card>

  <Card title="Talking avatar video" href="/api-reference/avatars/generate">
    Lip-sync pricing per second of audio.
  </Card>
</CardGroup>
