lowpoly retires Oct 30, 2026. Please switch models before these dates to avoid request errors. See all deprecations

Creative Lab Fidget Pixel API

Turn a photo into a multi-color, 3D-printable pixel-art fidget board in two stages. A prototype task turns the photo into a pixel-art image. A build task turns that image into interlocking pieces in a single 3MF file, and takes the prototype task's ID as input_task_id.

  • POST /openapi/creative-lab/fidget-pixel/v1/prototype
  • POST /openapi/creative-lab/fidget-pixel/v1/build

Both POST endpoints need a paid plan. A request from a free-plan account returns 402.


POST/openapi/creative-lab/fidget-pixel/v1/prototype

Create a Fidget Pixel prototype task

Generates one pixel-art image from a photo. To build the pieces, pass this task's ID to the build endpoint as input_task_id. For the task's fields, see The Fidget Pixel prototype task object.

If you don't like the image, create another prototype task. Each task is charged separately.

Request

POST
/openapi/creative-lab/fidget-pixel/v1/prototype
# Stage 1: pixelize the source photo
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/prototype \
  -X POST \
  -H "Authorization: Bearer ${YOUR_API_KEY}" \
  -H 'Content-Type: application/json' \
  -d '{
    "image_url": "<your publicly accessible image url or base64-encoded data URI>",
    "type": "person"
  }'

Response

{
  "result": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7"
}

Parameters

  • Name
    image_url
    Type
    string
    Required
    Description

    The photo to turn into pixel art, as a public URL or a data URI (data:image/jpeg;base64,<your base64-encoded image data>).

    The image must be a JPG, JPEG, PNG, or WebP file of at most 20 MB. Meshy detects the format from the image data, not from the file extension in the URL. A URL with no extension works, and Meshy follows redirects.

  • Name
    type
    Type
    string
    Required
    Description

    What the photo shows, which sets the pixel-art style. The two styles look clearly different. Any other value returns 400.

    Available values:

    • person: a person, in a portrait or a full-body photo. Meshy draws a chibi-style pixel sprite.
    • other: any other subject, for example a pet, an object, a mascot, a logo, or a landscape. Meshy draws a bead-art style pixel icon.
  • Name
    name
    Type
    string
    Description

    A name for the task, up to 100 characters. It comes back in the task's name field. A longer name returns 400.

Returns

The result field holds the ID of the new prototype task. Get the task or stream it until status is SUCCEEDED. Then pass the ID to the build endpoint as input_task_id.

Failure modes

  • Name
    400 - Bad Request
    Description

    The request can't run. For example:

    • image_url or type is missing.
    • type isn't person or other.
    • The image isn't a JPG, JPEG, PNG, or WebP file.
    • The image is too small, over 20 MB, or over the maximum pixel count.
    • Meshy can't download the image, for example because the URL returns 404 or times out.
    • The data URI isn't valid base64.
    • The image was flagged as NSFW.

    The error message says which problem it found. Fix the request, then send it again.

  • Name
    401 - Unauthorized
    Description

    The API key is missing or invalid. Check the Authorization header.

  • Name
    402 - Payment Required
    Description

    One of these applies:

    • The account doesn't have enough credits.
    • The API key, workspace, or member reached its credit limit.
    • The account is on the free plan.

    Check your credits and plan, then retry.

  • Name
    403 - Forbidden
    Description

    Your team has IP Filtering turned on, and Meshy flagged the image for third-party intellectual property. Send an image you have the rights to use.

  • Name
    429 - Too Many Requests
    Description

    One of these limits was reached:

    • You sent more requests than your rate limit allows. Wait a few seconds, then retry. See Rate limits.
    • Too many of your tasks are queued or running. Wait for some to finish, then retry.
    • On an unlimited plan with no credits left, you reached the number of tasks that can run without credits. Wait for a task to finish, or add credits.
  • Name
    500 - Internal Server Error
    Description

    Meshy hit a server error, for example because it couldn't run the intellectual property check on the image. Meshy doesn't create the task, and refunds any credits it charged. Retry the request.


POST/openapi/creative-lab/fidget-pixel/v1/build

Create a Fidget Pixel build task

Generates the 3D-printable pieces from a succeeded prototype task. Meshy samples the pixel-art image onto the grid and reduces it to at most color_count colors. Each cell of the subject becomes an interlocking piece, and the background isn't printed.

The result is a single multi-color 3MF file, ready to print in color. Each piece has a grey body and a top in its pixel's color. For its fields, see The Fidget Pixel build task object.

Request

POST
/openapi/creative-lab/fidget-pixel/v1/build
# Stage 2: chain build off a succeeded prototype task
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/build \
  -X POST \
  -H "Authorization: Bearer ${YOUR_API_KEY}" \
  -H 'Content-Type: application/json' \
  -d '{
    "input_task_id": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7",
    "options": {
      "shape": "square",
      "grid_size": 32,
      "piece_size_mm": 8,
      "color_count": 8,
      "piece_height_mm": 15
    },
    "output": {
      "format": "3mf"
    }
  }'

Response

{
  "result": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98"
}

Parameters

  • Name
    input_task_id
    Type
    string
    Required
    Description

    The ID of the prototype task to build. It must be a task your account created with POST /openapi/creative-lab/fidget-pixel/v1/prototype, with the status SUCCEEDED.

    An ID that isn't one of your Fidget Pixel prototype tasks from the API returns 404, including a prototype made in the web app.

  • Name
    name
    Type
    string
    Description

    A name for the task, up to 100 characters. It comes back in the task's name field. A longer name returns 400.

options

Settings for the shape and size of the pieces. Every field has a default, so send only the fields you want to change. A 0 uses the field's default. These are the same settings as in the Creative Lab web app. Other shape settings are fixed, and Meshy sets them from shape and piece_size_mm.

  • Name
    shape
    Type
    string
    default square
    Description

    The footprint of each piece. Any other value returns 400.

    Available values:

    • square: square pieces on a square grid
    • hex: hexagonal pieces on a hexagonal grid, with piece_size_mm set to 6 or 8
  • Name
    grid_size
    Type
    integer
    default 32
    Description

    The number of grid cells along each side of the board, 16 or 32. Any other value returns 400. A 32 grid keeps more detail. A 16 grid gives fewer pieces of the same size, so the board is about half as wide.

  • Name
    piece_size_mm
    Type
    integer
    default 8
    Description

    The edge length of each piece in millimeters, 6, 8, or 10. Any other value returns 400. With grid_size, it sets the size of the printed board. For example, a 32 grid of 8 mm pieces is about 26.5 cm per side.

    10 doesn't work with hex and returns 400, because the slanted hex face overhangs on most consumer FDM printers.

  • Name
    color_count
    Type
    integer
    default 8
    Description

    The maximum number of colors in the pixel-art palette, from 1 to 8. A value outside the range returns 400. To print, you need up to color_count plus one filaments, one per color and one for the grey bodies.

  • Name
    piece_height_mm
    Type
    integer
    default 15
    Description

    The height of each piece in millimeters, from 10 to 80. A value outside the range returns 400.

output

Settings for the result file.

  • Name
    format
    Type
    string
    default 3mf
    Description

    The file format of the result. Any other value returns 400.

    Available values:

    • 3mf: one multi-color model.3mf file, returned in model_urls.3mf

Returns

The result field holds the ID of the new build task. Get the task or stream it until status is SUCCEEDED. Then download the 3MF file from model_urls.3mf.

Failure modes

  • Name
    400 - Bad Request
    Description

    The request can't run. For example:

    • input_task_id is missing or isn't a valid task ID.
    • The prototype task's status isn't SUCCEEDED. If it's still running, wait and retry. If it failed or was canceled, create a new prototype task.
    • The prototype task succeeded but has no image. Create a new prototype task.
    • An options field is outside its allowed values, for example a grid_size other than 16 or 32.
    • piece_size_mm is 10 and shape is hex. Hex pieces come in 6 and 8 mm only.
    • output.format isn't 3mf.

    The error message says which problem it found.

  • Name
    401 - Unauthorized
    Description

    The API key is missing or invalid. Check the Authorization header.

  • Name
    402 - Payment Required
    Description

    One of these applies:

    • The account doesn't have enough credits.
    • The API key, workspace, or member reached its credit limit.
    • The account is on the free plan.

    Check your credits and plan, then retry.

  • Name
    403 - Forbidden
    Description

    Your team has IP Filtering turned on, and Meshy flagged the prototype task's image for third-party intellectual property. Create a prototype task from an image you have the rights to use.

  • Name
    404 - Not Found
    Description

    input_task_id doesn't match any Fidget Pixel prototype task your account created with the API. A prototype made in the web app also returns 404. Send the result ID from a prototype create response.

  • Name
    429 - Too Many Requests
    Description

    One of these limits was reached:

    • You sent more requests than your rate limit allows. Wait a few seconds, then retry. See Rate limits.
    • Too many of your tasks are queued or running. Wait for some to finish, then retry.
    • On an unlimited plan with no credits left, you reached the number of tasks that can run without credits. Wait for a task to finish, or add credits.
  • Name
    500 - Internal Server Error
    Description

    Meshy hit a server error, for example because it couldn't run the intellectual property check on the prototype task's image. Meshy doesn't create the task, and refunds any credits it charged. Retry the request.


GET/openapi/creative-lab/fidget-pixel/v1/(prototype|build)/:id

Get a Fidget Pixel task

Returns a prototype or build task. The path must match the task's stage. A build task requested through /prototype/:id returns 404, and so does a prototype task requested through /build/:id.

For the fields, see The Fidget Pixel prototype task object and The Fidget Pixel build task object.

Request

GET
/openapi/creative-lab/fidget-pixel/v1/prototype/019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7
# Prototype
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/prototype/019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

# Build
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/build/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

Prototype Response

{
  "id": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7",
  "type": "creative-lab-fidget-pixel-prototype",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001000000,
  "started_at": 1757001005000,
  "finished_at": 1757001178000,
  "expires_at": 1757260378000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 6,
  "image_urls": [
    "https://assets.meshy.ai/***/pixel-art.jpg?Expires=***"
  ]
}

Build Response

{
  "id": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98",
  "type": "creative-lab-fidget-pixel-build",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001300000,
  "started_at": 1757001304000,
  "finished_at": 1757001309000,
  "expires_at": 1757260509000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 30,
  "model_urls": {
    "3mf": "https://assets.meshy.ai/***/tasks/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/output/model.3mf?Expires=***"
  }
}

Parameters

  • Name
    id
    Type
    path
    Description

    The task ID from the create response.

Returns

The prototype task object or the build task object, matching the stage in the path.

Failure modes

  • Name
    400 - Bad Request
    Description

    id isn't a valid task ID. Send the result ID from the create response.

  • Name
    403 - Forbidden
    Description

    Your team has IP Filtering turned on, and Meshy flagged the prototype task's image for third-party intellectual property. Build tasks and teams on the Enterprise plan don't get this error. Create a prototype task from an image you have the rights to use.

  • Name
    404 - Not Found
    Description

    No Fidget Pixel task with this ID belongs to your account, or the task's stage doesn't match the path. Check the ID, and use /prototype or /build to match the task.

  • Name
    500 - Internal Server Error
    Description

    Meshy couldn't run the intellectual property check. Retry the request.


DELETE/openapi/creative-lab/fidget-pixel/v1/(prototype|build)/:id

Delete a Fidget Pixel task

Cancels a Fidget Pixel task. Canceling a PENDING task refunds its credits. An IN_PROGRESS task is canceled without a refund, because a worker has already started it. A task with a final status (SUCCEEDED, FAILED, or CANCELED) can't be canceled.

The path must match the task's stage. For example, DELETE on /prototype/:id with a build task's ID returns 404.

Request

DELETE
/openapi/creative-lab/fidget-pixel/v1/prototype/019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7
curl --request DELETE \
  --url https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/prototype/019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

Response

// Returns 204 No Content on success (empty body).

Path parameters

  • Name
    id
    Type
    path
    Description

    The ID of the task to cancel.

Returns

204 No Content with an empty body.

Failure modes

  • Name
    400 - Bad Request
    Description

    One of these applies:

    • id isn't a valid task ID. Send the result ID from the create response.
    • The task already has a final status (SUCCEEDED, FAILED, or CANCELED), so it can't be canceled.
  • Name
    404 - Not Found
    Description

    No Fidget Pixel task with this ID belongs to your account, or the task's stage doesn't match the path. Check the ID, and use /prototype or /build to match the task.


GET/openapi/creative-lab/fidget-pixel/v1/(prototype|build)/:id/stream

Stream a Fidget Pixel task

Streams a Fidget Pixel task's updates as Server-Sent Events (SSE). The path must match the task's stage. If it doesn't, for example a build task's ID at /prototype/:id/stream, the stream sends one error event with status_code: 404 and closes. An id that isn't a valid task ID does the same with status_code: 400.

Request

GET
/openapi/creative-lab/fidget-pixel/v1/build/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/stream
curl -N https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/build/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/stream \
-H "Authorization: Bearer ${YOUR_API_KEY}"

Response Stream

// Error event example (wrong stage or task not found)
event: error
data: {
  "status_code": 404,
  "message": "Task not found"
}

// Message event examples illustrate task progress.
// Every frame is the full task object for the stage; fields not yet populated are null / empty.
event: message
data: {
  "id": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98",
  "type": "creative-lab-fidget-pixel-build",
  "name": "",
  "status": "PENDING",
  "progress": 0,
  "created_at": 1757001300000,
  "started_at": null,
  "finished_at": null,
  "expires_at": 1757260500000,
  "preceding_tasks": 2,
  "task_error": null,
  "consumed_credits": 30,
  "model_urls": {}
}

event: message
data: {
  "id": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98",
  "type": "creative-lab-fidget-pixel-build",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001300000,
  "started_at": 1757001304000,
  "finished_at": 1757001309000,
  "expires_at": 1757260509000,
  "task_error": null,
  "consumed_credits": 30,
  "model_urls": {
    "3mf": "https://assets.meshy.ai/***/tasks/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/output/model.3mf?Expires=***"
  }
}

Parameters

  • Name
    id
    Type
    path
    Description

    The task ID from the create response.

Returns

A stream of prototype or build task objects. Each event has the full task object, as Get a Fidget Pixel task returns it. While the task is PENDING or IN_PROGRESS, its output fields are still empty (null, [], or {}), and finished_at is null.

If IP Filtering blocks the task's result, the stream sends one error event in its place, with the status_code that a get request would return.


GET/openapi/creative-lab/fidget-pixel/v1/(prototype|build)

List Fidget Pixel tasks

Returns your Fidget Pixel tasks of one stage, one page at a time. The path selects the stage. /prototype returns only prototype tasks, and /build returns only build tasks.

Request

GET
/openapi/creative-lab/fidget-pixel/v1/prototype
# List prototype tasks
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/prototype?page_size=10 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

# List build tasks
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/build?page_size=10 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

Response (List Prototype Tasks)

[
  {
    "id": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7",
    "type": "creative-lab-fidget-pixel-prototype",
    "name": "",
    "status": "SUCCEEDED",
    "progress": 100,
    "created_at": 1757001000000,
    "started_at": 1757001005000,
    "finished_at": 1757001178000,
    "expires_at": 1757260378000,
    "preceding_tasks": 0,
    "task_error": null,
    "consumed_credits": 6,
    "image_urls": [
      "https://assets.meshy.ai/***/pixel-art.jpg?Expires=***"
    ]
  }
]

Path parameters

  • Name
    stage
    Type
    path
    Required
    Description

    The stage to list, prototype or build.

Query parameters

  • Name
    page_num
    Type
    integer
    default 1
    Description

    The page to return, from 1 to 255. 0 returns the first page. A value outside 0 to 255 returns 400.

  • Name
    page_size
    Type
    integer
    default 10
    Description

    Number of tasks per page, up to 100. A value from 101 to 255 returns 100 tasks, and 0 uses the default. A value outside 0 to 255 returns 400.

  • Name
    sort_by
    Type
    string
    default -created_at
    Description

    The sort order.

    Available values:

    • -created_at: newest first
    • +created_at: oldest first

Returns

An array of prototype task objects or build task objects, matching the stage in the path.


The Fidget Pixel prototype task object

A prototype task turns a photo into a pixel-art image. To turn the image into 3D-printable pieces, pass the task's ID to the build endpoint as input_task_id.

Properties

Example Fidget Pixel Prototype Task Object

{
  "id": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7",
  "type": "creative-lab-fidget-pixel-prototype",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001000000,
  "started_at": 1757001005000,
  "finished_at": 1757001178000,
  "expires_at": 1757260378000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 6,
  "image_urls": [
    "https://assets.meshy.ai/***/pixel-art.jpg?Expires=***"
  ]
}
  • Name
    id
    Type
    string
    Description

    The task ID. Don't rely on its format.

  • Name
    type
    Type
    string
    Description

    The task type, always creative-lab-fidget-pixel-prototype.

  • Name
    name
    Type
    string
    Description

    The name you sent when you created the task, or "" if you didn't send one.

  • Name
    status
    Type
    string
    Description

    The task's status.

    Available values:

    • PENDING: waiting in the queue
    • IN_PROGRESS: generating the result
    • SUCCEEDED: final, with the result
    • FAILED: final, with the reason in task_error
    • CANCELED: final, with no result
  • Name
    progress
    Type
    integer
    Description

    Percentage of the work done, from 0 to 100. It's 0 until the task starts and 100 when it succeeds.

  • Name
    created_at
    Type
    timestamp
    Description

    Time the task was created, in Unix milliseconds.

  • Name
    started_at
    Type
    timestamp
    Description

    Time a worker started the task, in Unix milliseconds. null until then.

  • Name
    finished_at
    Type
    timestamp
    Description

    Time the task reached a final status, in Unix milliseconds. null until then.

  • Name
    expires_at
    Type
    timestamp
    Description

    Time the task's result expires, in Unix milliseconds. It's 3 days after finished_at, or 3 days after created_at until the task finishes. For Enterprise accounts, which keep API results indefinitely, it's about 100 years later. See Asset retention.

  • Name
    preceding_tasks
    Type
    integer
    Description

    The task's position in the queue while it's PENDING. 1 means it starts next. 0 after the task leaves the queue, and always 0 in list results.

  • Name
    task_error
    Type
    object
    Description

    Why the task failed. null unless status is FAILED. See Errors.

  • Name
    consumed_credits
    Type
    integer
    Description

    Number of credits charged for the task, after any refund. A request that returns an error isn't charged. A failed task is 0, because Meshy refunds it. Canceling refunds a task only while it's PENDING. If Meshy can't read the charge, a get or stream response leaves the field out, and a list request returns 500. In that case, retry the request.

  • Name
    image_urls
    Type
    array of strings
    Description

    Download URLs for the pixel-art image. The array has one URL after the task succeeds, and is empty until then.

    The URLs are signed, so fetch them without an Authorization header. They stay valid until expires_at. Getting the task again before then returns the same URLs, not newly signed ones. An expired URL can't be refreshed, so download and store the files before then.


The Fidget Pixel build task object

A build task generates the 3D-printable pieces from a succeeded prototype task. Meshy samples the pixel-art image onto the grid and returns the pieces in a single multi-color 3MF file.

Properties

Example Fidget Pixel Build Task Object

{
  "id": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98",
  "type": "creative-lab-fidget-pixel-build",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001300000,
  "started_at": 1757001304000,
  "finished_at": 1757001309000,
  "expires_at": 1757260509000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 30,
  "model_urls": {
    "3mf": "https://assets.meshy.ai/***/tasks/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/output/model.3mf?Expires=***"
  }
}
  • Name
    id
    Type
    string
    Description

    The task ID. Don't rely on its format.

  • Name
    type
    Type
    string
    Description

    The task type, always creative-lab-fidget-pixel-build.

  • Name
    name
    Type
    string
    Description

    The name you sent when you created the task, or "" if you didn't send one.

  • Name
    status
    Type
    string
    Description

    The task's status.

    Available values:

    • PENDING: waiting in the queue
    • IN_PROGRESS: generating the result
    • SUCCEEDED: final, with the result
    • FAILED: final, with the reason in task_error
    • CANCELED: final, with no result
  • Name
    progress
    Type
    integer
    Description

    Percentage of the work done, from 0 to 100. It's 0 until the task starts and 100 when it succeeds.

  • Name
    created_at
    Type
    timestamp
    Description

    Time the task was created, in Unix milliseconds.

  • Name
    started_at
    Type
    timestamp
    Description

    Time a worker started the task, in Unix milliseconds. null until then.

  • Name
    finished_at
    Type
    timestamp
    Description

    Time the task reached a final status, in Unix milliseconds. null until then.

  • Name
    expires_at
    Type
    timestamp
    Description

    Time the task's result expires, in Unix milliseconds. It's 3 days after finished_at, or 3 days after created_at until the task finishes. For Enterprise accounts, which keep API results indefinitely, it's about 100 years later. See Asset retention.

  • Name
    preceding_tasks
    Type
    integer
    Description

    The task's position in the queue while it's PENDING. 1 means it starts next. 0 after the task leaves the queue, and always 0 in list results.

  • Name
    task_error
    Type
    object
    Description

    Why the task failed. null unless status is FAILED. See Errors.

  • Name
    consumed_credits
    Type
    integer
    Description

    Number of credits charged for the task, after any refund. A request that returns an error isn't charged. A failed task is 0, because Meshy refunds it. Canceling refunds a task only while it's PENDING. If Meshy can't read the charge, a get or stream response leaves the field out, and a list request returns 500. In that case, retry the request.

  • Name
    model_urls
    Type
    object
    Description

    Download URLs for the result, keyed by file format. The object has one entry, for the output.format you sent, and is empty until the task succeeds.

    The URLs are signed, so fetch them without an Authorization header. They stay valid until expires_at. Getting the task again before then returns the same URLs, not newly signed ones. An expired URL can't be refreshed, so download and store the files before then.

    • Name
      3mf
      Type
      string
      Description

      Download URL for the multi-color 3MF file. Each piece has a grey body and a top in its pixel's color. Present when output.format is 3mf, which is the default.


End-to-end example

The following cURL and Python scripts run the whole flow. Each script creates a prototype task from a photo and waits for it to succeed. Then it creates a build task, waits for that task, and downloads the 3MF file from model_urls.3mf.

The cURL script needs curl and jq, and the Python script needs the requests package. To run a script, follow these steps:

  1. Set MESHY_API_KEY to your API key.
  2. Set IMAGE_PATH to the path of a local photo, or IMAGE_URL to a public image URL.
  3. Optional: To pixelize a subject other than a person, set PIXEL_TYPE to other.
  4. Run the script.

A prototype task usually finishes within a few minutes. A build task usually finishes in well under a minute.

In your app, show the image from image_urls to your user before you create the build task. If they don't like it, create another prototype task instead. This way, you spend build credits only on an image they approve.

Complete flow

POST
/openapi/creative-lab/fidget-pixel/v1
#!/usr/bin/env bash
set -euo pipefail

# Requires curl and jq. Point IMAGE_PATH at a local photo, or IMAGE_URL at a public one:
#   export MESHY_API_KEY=msy_...
#   export IMAGE_PATH=./portrait.jpg          # or: export IMAGE_URL=https://...
#   export PIXEL_TYPE=person                  # or: other
: "${MESHY_API_KEY:?export MESHY_API_KEY first}"
if [[ -z "${IMAGE_PATH:-}" && -z "${IMAGE_URL:-}" ]]; then
  echo "export IMAGE_PATH (local file) or IMAGE_URL (public url) first" >&2
  exit 1
fi
PIXEL_TYPE=${PIXEL_TYPE:-person}

BASE="https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1"
AUTH="Authorization: Bearer $MESHY_API_KEY"

# api METHOD URL [curl args...] -> prints the response body, non-zero on failure.
# Note we do not use -f/--fail: it discards the body, and the body is the only
# place the reason appears.
api() {
  local method=$1 url=$2 out http_code body
  shift 2
  out=$(curl --silent --show-error --max-time 60 --write-out $'\n%{http_code}' \
    -X "$method" "$url" -H "$AUTH" "$@") || return 1
  http_code=${out##*$'\n'}
  body=${out%$'\n'*}
  if ((http_code >= 400)); then
    echo "HTTP $http_code for $url: $body" >&2
    return 1
  fi
  printf '%s' "$body"
}

# Each task gets its own 40-minute budget.
poll() {
  local kind=$1 id=$2 delay=5 task_status deadline
  deadline=$(($(date +%s) + 2400))
  while :; do
    if (($(date +%s) >= deadline)); then
      echo "gave up waiting for $kind $id" >&2
      return 1
    fi
    task_status=$(api GET "$BASE/$kind/$id" | jq -r '.status')
    echo "$kind: $task_status"
    case "$task_status" in
    SUCCEEDED) return 0 ;;
    FAILED | CANCELED) return 1 ;;
    esac
    sleep "$delay"
    delay=$((delay * 2 > 30 ? 30 : delay * 2))
  done
}

# Build the request body in a file. A base64 data URI must never go on the
# command line or into an exported variable - a photo of any real size will
# exceed the OS argument limit.
BODY=$(mktemp)
trap 'rm -f "$BODY"' EXIT
if [[ -n "${IMAGE_PATH:-}" ]]; then
  # Declare the real type: the API accepts JPEG, PNG and WebP.
  case "$(printf '%s' "${IMAGE_PATH##*.}" | tr 'A-Z' 'a-z')" in
    png) MIME=image/png ;;
    webp) MIME=image/webp ;;
    *) MIME=image/jpeg ;;
  esac
  {
    printf '{"type":"%s","image_url":"data:%s;base64,' "$PIXEL_TYPE" "$MIME"
    base64 <"$IMAGE_PATH" | tr -d '\n'
    printf '"}'
  } >"$BODY"
else
  jq -n --arg t "$PIXEL_TYPE" --arg u "$IMAGE_URL" \
    '{type: $t, image_url: $u}' >"$BODY"
fi

# 1. Create the prototype task
PROTO_ID=$(api POST "$BASE/prototype" \
  -H 'Content-Type: application/json' --data-binary @"$BODY" | jq -r '.result')

# 2. Wait for the pixel-art image (show image_urls[0] to a user in production)
poll prototype "$PROTO_ID"

# 3. Create the build task (defaults: square pieces, 32x32 grid, 8 mm, 8 colors, 15 mm tall)
jq -n --arg p "$PROTO_ID" \
  '{input_task_id: $p, options: {shape: "square", grid_size: 32, piece_size_mm: 8, color_count: 8, piece_height_mm: 15}, output: {format: "3mf"}}' >"$BODY"
BUILD_ID=$(api POST "$BASE/build" \
  -H 'Content-Type: application/json' --data-binary @"$BODY" | jq -r '.result')

# 4. Wait for the pieces
poll build "$BUILD_ID"

# 5. Download the 3MF. This is a signed URL: no Authorization header,
#    and it stays valid for 3 days after the task finishes.
TASK=$(api GET "$BASE/build/$BUILD_ID")
curl --silent --show-error --fail --max-time 900 \
  -o fidget-pixel.3mf "$(jq -r '.model_urls["3mf"]' <<<"$TASK")"
echo "Done: fidget-pixel.3mf"