API reference

Three endpoints: submit an image, poll the job, download the model. Everything the website can do, your own code can do too.

Beep boop?

There is a page for autonomous agents, with a ready-made skill file and machine-readable copies of everything below: img2model for agents.

1. Get an API key

  1. Create a free account and verify your email - credits arrive immediately, and top up again every week.
  2. Open your profile and generate an API key.
  3. Send it in the body of every API call as 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.

2. The whole loop

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.

3. Endpoints

POST /api/jobs/new - Submit a job

Uploads an image and queues it. Costs credits the moment it is accepted. Encoding: multipart/form-data (the image is a file upload).

ParameterMeaning
api_keyYour 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 job

Where the job is, and - once it is done - what came back. Poll this until `finished` is true. Encoding: application/x-www-form-urlencoded.

ParameterMeaning
api_keyYour 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 result

Streams the finished model. Only valid once the job is COMPLETED. Encoding: application/x-www-form-urlencoded.

ParameterMeaning
api_keyYour 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.

4. Responses

Submission response

KeyTypeMeaning
successboolWhether the job was created. Check this, not the HTTP status.
messagestringWhy not, when success is false. May contain HTML line breaks when it is a list of validation errors.
job_idstring|nullUUID of the new job. This is what you poll and download with.
remaining_creditsintYour balance after the job was charged.

Status response

KeyTypeMeaning
successboolWhether the job could be read. False for an unknown id, someone else's job, or a bad key.
messagestringWhy not, when success is false.
idstring|nullThe job id.
statestring|nullOne of NEW, QUEUED, REJECTED, IN_PROGRESS, COMPLETED, FAILED.
state_hrstring|nullThe same state written for a person.
finishedbool|nullTrue once the job will not change again. True for FAILED and REJECTED as well - a loop that waits for COMPLETED alone waits forever.
formatstring|nullglb or vox. Decides what the downloaded bytes are.
bytesint|nullSize of the stored result. Null until it is on disk.
verticesint|nullVertices delivered. Null for a vox result, which is not a mesh.
facesint|nullFaces delivered. Compare it against the polycount you asked for: a large shortfall is a thin result, not a rounding difference.

A refusal is an HTTP 200 with 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.

5. Job fields

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 job

The whole surface. These are exactly the controls the website's own generator has, and every one of them is optional except the image.

FieldAcceptedDefaultNotes
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.

Model types

The values mode takes. Set this and leave the rest at their defaults unless you have a reason not to.

modeCalledWhat you get
full_pbrFull PBRMeasured metallic and roughness on top of the colour. The best materials, and about twice the wait.
simple_pbrBasic PBRColour plus a metallic/roughness map. The default, and the fastest way to a usable material.
albedo_onlyBasicColour only, no material map. The honest option for photographs, where a guessed metallic map reads as dull chrome.
texturelessTexturelessBare geometry, no colour at all. The fastest and cheapest option.

Plan limits

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.

PlanPolygonsTexturesVoxelsFull PBRQueue 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.

6. Rules that are not visible in any one field

  • One job at a time. A submission is refused while any earlier job of yours is still NEW, QUEUED or IN_PROGRESS. Poll the previous job to `finished` before submitting the next.
  • Failed jobs are free. Credits are charged at submission and a job that ends FAILED or REJECTED stops counting against your balance, which is the refund. Nothing needs to be claimed.
  • A mode wins over the switches it stands for. `mode` is a name for the texture settings, so posting `mode=full_pbr` together with `auto_texture=0` gives you the mode - textures and all. Ask for `mode=textureless` when you want bare geometry.
  • Every mode goes up to a million faces. No mode is capped below the `polycount` range, and a dense mesh costs storage rather than much more time. What the *account* may order is a separate thing - see the plan table below.
  • Your plan caps polycount, texture size and Full PBR. The free plan stops at 250,000 faces, 2048 textures, 256 voxels and simple_pbr. Going over is a refusal, not a silently reduced job, and the `message` says which plan lifts it. Nothing is charged for a refused submission.
  • Unknown fields are an error. Posting a name this page does not list fails the whole submission with "This form should not contain extra fields" rather than ignoring it - so a typo cannot silently cost you a job at the defaults. Names retired from earlier versions of this API are the exception, and are accepted and dropped so that older clients keep working: `smoothness` still means `polycount`, `remove_background` is now always done, and the per-stage tuning parameters are no longer part of the public API.

7. What a job costs

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.

  • Cheapest possible job: 39 credits
  • Default job: 52 credits, about 41 seconds
  • Turning textures off or dropping the detail level is where the real savings are

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.

8. When it goes wrong

What you seeWhat it means
Wrong, missing or outdated API key providedThe 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 queueAn 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 creditsCheck your balance. Free credits top up weekly.
This form should not contain extra fieldsA 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 jobThe generator rejected the parameters, or returned something that was not a model. The credits are already back.

9. Blender, without writing any of this

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.