December 2, 2020 update:
  • The Poly website ( and Poly API will be shut down on June 30, 2021. Uploading of new assets will be disabled two months earlier, on April 30, 2021.
  • Previously uploaded assets will remain available and can be downloaded from until June 30, 2021.

Poly API Guide

The Poly API provides read access to assets hosted on Poly to all, and upload access to Poly for whitelisted accounts.


All calls to the Poly API must include credentials. Credentials are used by Poly to identify your app and enforce usage limits. Credentials for the Poly API, like other Google APIs, are managed through the Google APIs Console. Follow these steps to get credentials:

Enable the Poly API

In the Google APIs Console, go to the Poly API and click Enable. Once it is enabled, you can monitor usage and manage access in the API Dashboard.

Create credentials

In the Google APIs Console, go to the Credentials and create credentials for your app. There are different types of credentials:

  • API Key: provides access to published assets and other resources that don't require user authentication. The API key must be included in calls to the API. We also provide a wizard to quickly get an API key:

    Get an API key

  • OAuth client ID: provides access to the published assets (like an API key) and also, allows authenticated users to access their private assets or upload new assets. Your app must implement OAuth 2.0, which lets users sign in to their account and grant access to your app via the Google Authorization Server. Once access is granted, you receive an access token which must be included in calls to the API. For read access to user's private assets, your app must request the following scopes:



    To upload an asset on behalf of a user, your app must additionally request the following scopes:

    • openid


Request upload access (Optional)

To upload assets with the Poly API, you must first request access. Click the button below and submit the form:

Request Access

After you are approved, you'll be able to upload an asset.

Usage and quota

The dashboard in the Google APIs Console lets you monitor and manage access to the Poly API.

The Poly API has a default quota of 3000 queries-per-minute (QPM), per API key or client id. Apps that send more than 3000 QPM will have all requests beyond this limit rejected. These requests should be retried, and calling code should be modified to avoid sending more than 3000 QPM.

HTTP GET requests for resource files that make up an asset, such as a thumbnail or texture, do not count against this quota.

To request additional quota, fill out and submit the Poly API Quota Request.

API overview

The API allows you to:

To try out the Poly API, we recommend the APIs explorer—a tool that helps you explore various Google APIs interactively. For more information, see docs for APIs explorer.

Upload an asset

Poly API lets you upload OBJ assets to Poly on behalf of an authenthicated user. Uploaded assets will appear in a user's Your Uploads area. This allows you to build apps and plugins, like the Poly plugins, which let you upload assets to Poly from 3ds Max, Maya, and Blender.

Here's a summary of the upload process:

  • Create OAuth credentials.

  • Request access to the Upload API.

  • Implement OAuth 2.0. Once a user signs in, you'll get an access token that is used to identify the user in subsequent calls to the API.

  • Upload files that make up the 3D model (OBJ, MTL, PNGs, etc). For each uploaded file, the server will return an element ID.

  • Call start import with the list of element IDs from the previous step. The server needs time to parse and convert the OBJ model into a Poly asset, so it returns an Operation ID that lets you track the progress of the operation.

  • Poll the server periodically with the operation ID to check whether it is complete. When complete, the done property is set to true and the response will contain the uploaded asset's URL. You can direct your user to the URL in a browser where they'll be able to review, tweak, and publish the asset.

Upload files

Upload the individual files that make up the 3D model (OBJ, MTL, PNGs, etc) to Poly by making a POST request to following URL for each file:

The body of the request should be:

Authorization: Bearer ACCESS_TOKEN_HERE
Content-Type: multipart/form-data; boundary=SomeLongBoundaryToken
Content-Length: 1234

Content-Disposition: form-data; name="file"; filename="filename.ext"
Content-Type: mime-type

file contents here


Replace the placeholders above:

  • ACCESS_TOKEN_HERE: the access token you got from the OAuth process.
  • SomeLongBoundaryToken: Replace by something longer and statistically unlikely to occur in the file contents. The boundary token normally starts with lots of dashes (-) and any occurence of it in the body is required to be preceded by two dashes. The terminating boundary token is also terminated with an additional two dashes.
  • 1234: replace by the actual content length of your full request
  • filename.ext: replace by the name of your file, like model.obj.
  • file contents: the actual content of your file
  • mime-type: the MIME-type for your file

Here are some frequently used MIME types in OBJ models:

  • OBJ files: text/plain
  • MTL files (materials): text/plain
  • PNG files: image/png
  • JPG textures: image/jpeg

The response will be a JSON object. The important field is elementId, which identifies the file you have just uploaded:

  "elementId": "SomeElementId",
  ...(other stuff)...

Save each elementID; you'll need them for the next step.

Start import

After you uploaded the individual files that make up the model, you now have a collection of element IDs. You must now call startImport with the list of element IDs. This tells Poly to parse and convert the OBJ model into a Poly asset.

To do that, make a POST request (not a GET request!) to:

The request body should be:

  "importFormat": {
    "root": "element ID of OBJ file goes here",
    "resources": [
       "element ID of resource file",
       "element ID of resource file",
       "element ID of resource file",
  "displayName": "Your suggested display name",
  "description": "Your suggested description"

Note that the element ID of the OBJ file should go under root, while the element ID of the remaining files (MTL, PNG, etc) should go under the resources array.

This API call is asynchronous. The result will be an Operation object representing the ongoing import operation. Here is the full format:

  "name": "SomeOperationName",
  "done": true | false,
  "error": {
    "code": "SomeErrorCode",
    "message": "SomethingExplainingWhatBlewUpAndWhy",
  "response": {
    "assetId": "SomeAssetId",
    "publishUrl": "SomePublishUrl",
    "assetImportMessages": [
         // see "error reporting" below.
  "name": "SomeOperationName"

Here is how to interpret it:

  • If the error field is present, something went wrong (see the Errors section below).

  • Else, if the done field is false or missing, this means that the operation isn't done yet. In this case, wait a few seconds and poll (see the Polling section below).

  • Else (done is true), there will be a response field. This means that the import process has finished. The response field indicates the result.

    • If the response field has a publishUrl field, the operation was successful. You can direct your user to the URL in a browser where they'll be able to view, tweak, and publish their asset.

    • If there is no publishUrl field, something went wrong. Check assetImportMessages for errors.

Poll the server

Unless your file is extremely simple, chances are that the response you get from the startImport request won't have the done field set to true, meaning that the import operation isn't done yet.

In this case, you should wait a few seconds and ask about the status of the operation. To ask about the status, send a POST request to the following URL:

Replace SomeOperationName with the name you got from calling startImport.

The response to this request will again be the Operation object, possibly this time with the done field set to true. If it's not done yet, wait a few more seconds, and ask again.


Here are three possible errors (ordered from general errors to specific errors):

  • The HTTP response is something other than 200 OK. This means a high-level protocol error happened. Something like a malformed HTTP request, bad authentication credentials, you tripped on your network cable, or there was some horrible server error on our side while processing your request.

  • The HTTP request succeeds, but the response (Operation object) has an error field in the JSON. This means a reasonably generic error happened while processing the files.

  • The response (Operation object) includes an assetImportMessages array. The messages will indicate what went wrong. For example:

    "assetImportMessages": [
        "code": "SomeMessageCode",
        "objParseError": {
           "code": "SomeObjParseErrorCode",
           "filePath": "filename.ext",
           "lineNumber": "LineNumberWhereErrorHappened",
           "startIndex": "CharacterIndexWhereErrorStarts",
           "endIndex": "CharacterIndexWhereErrorEnds",
           "line": "TheOffendingLine"
        "imageError": {
           "code": "SomeImageErrorCode",
           "filePath": "someimage.ext"

If you need more help, check out the Poly Product Forum, a place to ask questions, discover tips, and connect with other Poly users.