API reference
Estimate
POST /v1/estimate tells you what a request would cost and produce, without sending or processing a file.
Endpoint
POST/v1/estimate
You describe a file and the request you plan to make. The API answers with the format the result would have, whether it can go as a direct request or needs a job, and what it would cost. No file is sent, nothing is processed, and the estimate itself is free. It works with a live key and with a test key.
Request
Send a JSON body. A field that is not listed here is refused with bad_request.
| Field | Type | Description |
|---|---|---|
| operation * | string | compress, convert or strip_metadata. |
| filename * | string | The name of the file. Its extension says what kind of file to estimate, because there are no bytes to inspect. |
| size * | integer | The size of the file in bytes. A whole number greater than 0. |
| duration_seconds | number | The length of the audio or video in seconds. Required for audio and video, which are priced by the minute. |
| pages | integer | The number of pages of a PDF. Used when compressing a PDF, which is priced per started 100 pages. If it is left out, 100 pages or fewer is assumed. |
| width | integer | The width of a video in pixels. If width or height is left out, 1920 by 1080 is assumed. |
| height | integer | The height of a video in pixels. |
| options | object | The options you plan to send, with the same shape and rules as options on a job. For convert, options.convert.to is required. |
Response
Status 200.
{
"object": "estimate",
"operation": "compress",
"kind": "image",
"input_format": "jpg",
"output_format": "avif",
"lane": "direct",
"meter": "images",
"quantity": 1,
"amount_usd": 0.01,
"notes": [
"If the file cannot be made smaller it is returned unchanged and the request is free."
]
}| Field | Type | Description |
|---|---|---|
| object | string | Always "estimate". |
| operation | string | The operation you sent. |
| kind | string | The kind of file the extension stands for: image, pdf, video, audio, word, sheet, presentation, table or font. |
| input_format | string | The format the extension stands for, for example jpg. |
| output_format | string | The format the result is expected to have. |
| lane | string | direct when the file can be sent to a direct endpoint: its kind is accepted there and its size is within the direct limit. Otherwise job. |
| meter | string | The billing meter the request would be charged on: images, pdfs, documents, audio_minutes, video_units or files. See Billing. |
| quantity | number | The number of units on that meter. |
| amount_usd | number | The price in US dollars, at the unit prices. For images it takes into account how many images the account has already been billed for this month. |
| notes | array of strings | What was assumed, and anything that would stop or change the real request. Read them: an estimate with a note may not be what you get. |
| Note | Meaning |
|---|---|
| width and height were not given, so a 1920x1080 source was assumed. | Send width and height for an exact video price. |
| pages was not given, so 100 pages or fewer was assumed. | Send pages for an exact PDF price. |
| Video is in limited preview and is not enabled for this account. | The real request would fail until support enables video. |
| HEIC output is in limited preview and is not enabled for this account. | The real request would fail until support enables HEIC output. |
| The file is larger than this plan allows. | The real request would be refused for its size. |
| If the file cannot be made smaller it is returned unchanged and the request is free. | On every compress estimate. The price is what a successful compression costs. |
How the estimate is made
- From the name, not the content. The kind of file is read from the extension of
filename. The real request reads it from the file's content. If the two disagree, the real request follows the content. - As if it succeeds. The amount is the charge for a successful request. A request that fails, or that returns the input unchanged, costs nothing.
- Video. The price follows the size of the output. For a compress estimate,
video.resolutionis applied to the width and height you send, andvideo.codecandvideo.formatdecide the codec. - Limits are not checked, apart from the file size. A file over a pixel, page or length limit gets an estimate and then fails as a real request.
- Spend cap and plan credit are not applied. The amount is the value of the usage, not what your next invoice will add.
Errors
| Code | Status | When |
|---|---|---|
bad_request | 400 | The body is not a JSON object or has an unknown field. operation, filename or size is missing or not valid. A number is not positive. duration_seconds is missing for audio or video. |
invalid_options | 400 | An option is unknown or has a value that is not allowed. A convert estimate has no options.convert.to. |
unsupported_input | 415 | The extension is not one the API handles, or the operation does not apply to that kind of file. |
unsupported_target | 415 | The convert target is not available for that kind of file. |
missing_key, invalid_key, revoked_key | 401 | The API key is missing, wrong or revoked. |
no_payment_method, payment_required | 402 | A live key on an account with no plan, or with an unpaid invoice. |
account_suspended, ip_not_allowed | 403 | The account is suspended, or the key may not be used from this IP address. |
Examples
The price of compressing a JPEG to AVIF:
curl -X POST "https://api.smolmac.com/v1/estimate" \
-H "Authorization: Bearer $SMOL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation": "compress",
"filename": "photo.jpg",
"size": 1614027,
"options": { "image": { "format": "avif" } }
}'const response = await fetch("https://api.smolmac.com/v1/estimate", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMOL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
operation: "compress",
filename: "photo.jpg",
size: 1614027,
options: { image: { format: "avif" } },
}),
});
const estimate = await response.json();
if (!response.ok) throw new Error(`${estimate.error.code}: ${estimate.error.message}`);
console.log(estimate.lane, estimate.amount_usd, estimate.notes);import os
import requests
response = requests.post(
"https://api.smolmac.com/v1/estimate",
headers={"Authorization": f"Bearer {os.environ['SMOL_API_KEY']}"},
json={
"operation": "compress",
"filename": "photo.jpg",
"size": 1614027,
"options": {"image": {"format": "avif"}},
},
)
estimate = response.json()
if not response.ok:
raise RuntimeError(f"{estimate['error']['code']}: {estimate['error']['message']}")
print(estimate["lane"], estimate["amount_usd"], estimate["notes"])The price of compressing a video of 2 minutes 30 seconds at 1920 by 1080, with the default codec, H.265. Three started minutes, times 14 units for 1080, times 1.5 for H.265, is 63 units:
{
"operation": "compress",
"filename": "talk.mov",
"size": 262144000,
"duration_seconds": 150,
"width": 1920,
"height": 1080
}{
"object": "estimate",
"operation": "compress",
"kind": "video",
"input_format": "mov",
"output_format": "mp4",
"lane": "job",
"meter": "video_units",
"quantity": 63,
"amount_usd": 0.1575,
"notes": [
"If the file cannot be made smaller it is returned unchanged and the request is free."
]
}