Three endpoints: submit an image, poll the job, download the model. Everything the website can do, your own code can do too.
There is a page for autonomous agents, with a ready-made skill file and machine-readable copies of everything below: img2model for agents.
api_key.
Keys are free, start with I2M_ and are 52 characters long. Generating a new one revokes the
previous one immediately. Keep it to yourself: anyone holding your key spends your credits.
There is no endpoint that creates an account or a key - both need a signed-in browser, on purpose.
Submit, poll until it is finished, download. Nothing else is required.
KEY=I2M_your_key_here
# submit
curl -X POST https://img2model.com/api/jobs/new \
-F "api_key=$KEY" \
-F "new_job_form[image]=@chair.png" \
-F "new_job_form[mode]=simple_pbr" \
-F "new_job_form[detail]=3" \
-F "new_job_form[polycount]=25000"
# {"success":true,"message":"Job created","job_id":"019fd4...","remaining_credits":471}
# poll every few seconds until "finished":true
curl -X POST https://img2model.com/api/jobs/status/019fd4... -d "api_key=$KEY"
# {"success":true,"state":"IN_PROGRESS","state_hr":"Being processed","finished":false,...}
# download
curl -X POST https://img2model.com/api/jobs/result/019fd4... -d "api_key=$KEY" -o chair.glb
A default job takes about 41 seconds. Poll every 3-10 seconds - the API has no push channel, and hammering it does not make the GPU faster.
POST /api/jobs/new - Submit a jobUploads an image and queues it. Costs credits the moment it is accepted. Encoding: multipart/form-data (the image is a file upload).
| Parameter | Meaning |
|---|---|
api_key | Your key. In the body, never the query string. |
new_job_form[image] | The source image. Required. |
new_job_form[…] | Any of the job fields below. All optional; each one left out means the documented default. |
Returns the submission response below.
POST /api/jobs/status/{id} - Poll a jobWhere the job is, and - once it is done - what came back. Poll this until `finished` is true. Encoding: application/x-www-form-urlencoded.
| Parameter | Meaning |
|---|---|
api_key | Your key. |
{id} | The `job_id` returned by /api/jobs/new, in the path. |
Returns the status response below.
POST /api/jobs/result/{id} - Download the resultStreams the finished model. Only valid once the job is COMPLETED. Encoding: application/x-www-form-urlencoded.
| Parameter | Meaning |
|---|---|
api_key | Your key. |
{id} | The job id, in the path. |
Returns the file itself, with the format's own Content-Type and a filename ending in
.glb or .vox. A job that is not finished - or not yours -
gets a JSON refusal instead, so check whether the first byte is { before writing the
body to disk.
| Key | Type | Meaning |
|---|---|---|
success | bool | Whether the job was created. Check this, not the HTTP status. |
message | string | Why not, when success is false. May contain HTML line breaks when it is a list of validation errors. |
job_id | string|null | UUID of the new job. This is what you poll and download with. |
remaining_credits | int | Your balance after the job was charged. |
| Key | Type | Meaning |
|---|---|---|
success | bool | Whether the job could be read. False for an unknown id, someone else's job, or a bad key. |
message | string | Why not, when success is false. |
id | string|null | The job id. |
state | string|null | One of NEW, QUEUED, REJECTED, IN_PROGRESS, COMPLETED, FAILED. |
state_hr | string|null | The same state written for a person. |
finished | bool|null | True once the job will not change again. True for FAILED and REJECTED as well - a loop that waits for COMPLETED alone waits forever. |
format | string|null | glb or vox. Decides what the downloaded bytes are. |
bytes | int|null | Size of the stored result. Null until it is on disk. |
vertices | int|null | Vertices delivered. Null for a vox result, which is not a mesh. |
faces | int|null | Faces delivered. Compare it against the polycount you asked for: a large shortfall is a thin result, not a rounding difference. |
success: false
Bad key, unknown job id, someone else's job, an impossible combination of settings, an empty balance -
all of them arrive as a 200 carrying success: false and a message. Treat a
non-2xx status as a transport problem and success as the verdict, in that order.
One wart to code around: when /api/jobs/status/{id} rejects your key, it answers in
the submission shape (job_id, remaining_credits), so state and
finished are missing rather than null. Check success first, always.
Everything describing the job is posted under the new_job_form[…] prefix, because the API and
the website submit the same form. Only the image is required; every field left out means the default in
these tables, not null.
The whole surface. These are exactly the controls the website's own generator has, and every one of them is optional except the image.
| Field | Accepted | Default | Notes |
|---|---|---|---|
image |
image/png, image/jpeg, image/gif, image/webp, up to 20 MB | required | The picture to model. A photo, a render or a sketch all work; a subject that is already three-dimensional works far better than a logo. |
mode |
full_pbr, simple_pbr, albedo_only, textureless | simple_pbr | What kind of model you want, and the one field to set if you only set one. Listed in full below. |
detail |
1-5 | 3 | How hard the generator works on the shape (Draft, Fast, Standard, High, Ultra). The single biggest lever on both price and wait. |
polycount |
1000-1000000 | 25000 | Target face count of the delivered mesh. It is a target, not a promise - results land within a few percent. Denser meshes are barely slower but a lot larger; the website offers 1,000, 2,000, 5,000, 10,000, 25,000, 50,000, 100,000, 200,000, 500,000, 1,000,000. |
auto_texture |
1 / 0 (also accepts on, off, true, false) | on | Whether textures are generated at all. Off gives bare geometry - faster, cheaper, and what you want for 3D printing. |
texture_size |
1 = 1024, 2 = 2048, 3 = 4096 | 2 (2048) | A step, not a pixel count: posting 2048 here is not the 2048 texture, it is out of range. Ignored when auto_texture is off. |
format |
glb, vox | glb | glb is the mesh. vox is a MagicaVoxel model, generated as voxels rather than converted from the mesh, and it carries its own per-voxel materials - so the texture settings do not apply to it. |
voxel_size |
10-1024 | 64 | A ceiling on the longest axis, not a cube: the grid is sparse and non-cubic, so the other axes come out smaller. Only applies to a vox result, and it is the only plan ceiling that does - polycount and texture size describe a mesh a voxel job does not have. |
The values mode takes. Set this and leave the rest at their defaults unless you have a reason not to.
| mode | Called | What you get |
|---|---|---|
full_pbr | Full PBR | Measured metallic and roughness on top of the colour. The best materials, and about twice the wait. |
simple_pbr | Basic PBR | Colour plus a metallic/roughness map. The default, and the fastest way to a usable material. |
albedo_only | Basic | Colour only, no material map. The honest option for photographs, where a guessed metallic map reads as dull chrome. |
textureless | Textureless | Bare geometry, no colour at all. The fastest and cheapest option. |
Your plan caps what you may order. Going over is a refusal rather than a quietly reduced job, and
nothing is charged for it - the message names the setting and the plan that lifts it.
| Plan | Polygons | Textures | Voxels | Full PBR | Queue priority |
|---|---|---|---|---|---|
| Free trial | 250,000 | 2048 x 2048 | 256 | no | no |
| Regular | 500,000 | 4096 x 4096 | 512 | yes | no |
| Pro | 1,000,000 | 4096 x 4096 | 1024 | yes | yes (not live yet) |
The same job costs the same credits on every plan - a plan only moves the ceilings. See your plan.
Credits are charged when the job is accepted, and the price is the estimated GPU time plus a little for storing the result. A job that ends failed or rejected costs nothing - there is nothing to claim.
There is no price-quote endpoint for API clients - the website's calculator is browser-only and
CSRF-protected. Read remaining_credits off the submission response, or check your
billing page.
| What you see | What it means |
|---|---|
Wrong, missing or outdated API key provided | The key was missing, too short, or matches no account. It goes in the body - a ?api_key= query string authenticates as nobody. |
You already have a job in queue | An earlier job of yours has not finished. Poll it first. |
… available on the Regular plan and up … | The settings are over your plan's ceiling. Lower the setting the message names, or upgrade. Nothing was charged. |
Not enough credits | Check your balance. Free credits top up weekly. |
This form should not contain extra fields | A field name that is not in section 5 - usually a typo, or a missing new_job_form[…] prefix. |
Could not find job with id … | Unknown id, or a job belonging to another account. The two are deliberately indistinguishable. |
… is not finished yet! | You asked for the result before finished was true. |
a FAILED job | The generator rejected the parameters, or returned something that was not a model. The credits are already back. |
The add-on is this API with a panel on it: generate meshes from images inside Blender, and texture existing meshes automatically. Follow the installation guide.