I am writing a crawler that looks through all the Figma files in a project and finds a certain kind of node. I am using this npm package --> https://www.npmjs.com/package/figma-api which uses the Figma REST API under the hood.
Looking at the API documentation You have to use the GET file endpoint which:
Returns the document referred to by :key as a JSON object.
This JSON is a tree of Nodes which represents the entire Figma File.
One of my files is quite large and when I try to fetch it I get the following error:
Error: Request failed with status code 400
at createError (/workspaces/figmaDesignTracker/node_modules/axios/lib/core/createError.js:16:15)
at settle (/workspaces/figmaDesignTracker/node_modules/axios/lib/core/settle.js:17:12)
at IncomingMessage.handleStreamEnd (/workspaces/figmaDesignTracker/node_modules/axios/lib/adapters/http.js:269:11)
at IncomingMessage.emit (node:events:525:35)
at IncomingMessage.emit (node:domain:489:12)
at endReadableNT (node:internal/streams/readable:1359:12)
at processTicksAndRejections (node:internal/process/task_queues:82:21) {
config: {
url: 'https://api.figma.com/v1/files/<FILE-ID>?',
headers: {
Accept: 'application/json, text/plain, */*',
'X-Figma-Token': '<API KEY>',
'User-Agent': 'axios/0.21.4'
},
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus],
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
method: 'get',
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
finish: [Function: requestOnFinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
secureConnecting: false,
_SNICallback: null,
servername: 'api.figma.com',
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object: null prototype],
_eventsCount: 10,
connecting: false,
_hadError: false,
_parent: null,
_host: 'api.figma.com',
_closeAfterHandlingError: false,
_readableState: [ReadableState],
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular *1],
[Symbol(res)]: [TLSWrap],
[Symbol(verified)]: true,
[Symbol(pendingSession)]: null,
[Symbol(async_id_symbol)]: 5,
[Symbol(kHandle)]: [TLSWrap],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object]
},
_header: 'GET /v1/files/<FILE-ID>? HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'X-Figma-Token: <API KEY>\r\n' +
'User-Agent: axios/0.21.4\r\n' +
'Host: api.figma.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype],
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
joinDuplicateHeaders: undefined,
path: '/v1/files/<FILE-ID>?',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
socket: [TLSSocket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [Array],
rawTrailers: [],
joinDuplicateHeaders: undefined,
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 400,
statusMessage: 'Bad Request',
client: [TLSSocket],
_consuming: false,
_dumped: false,
req: [Circular *1],
responseUrl: 'https://api.figma.com/v1/files/<FILE-ID>?',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 28,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'api.figma.com',
protocol: 'https:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: 'https://api.figma.com/v1/files/<FILE-ID>?',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kEndCalled)]: true,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'x-figma-token': [Array],
'user-agent': [Array],
host: [Array]
},
[Symbol(errored)]: null,
[Symbol(kUniqueHeaders)]: null
},
response: {
status: 400,
statusText: 'Bad Request',
headers: {
'content-type': 'application/json; charset=utf-8',
'content-length': '77',
connection: 'close',
date: 'Thu, 04 May 2023 13:38:37 GMT',
'access-control-allow-origin': '*',
'access-control-allow-headers': 'Content-Type, X-Figma-Token, Authorization',
'cache-control': 'no-cache, no-store',
vary: 'X-Figma-Token,Authorization',
'x-cache': 'Error from cloudfront',
via: '1.1 ed9cefde6d1b28548496972ee2c72448.cloudfront.net (CloudFront)',
'x-amz-cf-pop': 'LHR50-P8',
'alt-svc': 'h3=":443"; ma=86400',
'x-amz-cf-id': 'ibKLFlSBQAKcnHxA_G3wgrS1RtmUHpmsPQCF4DE7tIIKsA4RbYCsjQ==',
'strict-transport-security': 'max-age=31536000; includeSubDomains; preload'
},
config: {
url: 'https://api.figma.com/v1/files/<FILE-ID>?',
headers: [Object],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus],
transitional: [Object],
method: 'get',
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: [TLSSocket],
_header: 'GET /v1/files/<FILE-ID>? HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'X-Figma-Token: <API KEY>\r\n' +
'User-Agent: axios/0.21.4\r\n' +
'Host: api.figma.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
joinDuplicateHeaders: undefined,
path: '/v1/files/<FILE-ID>?',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'api.figma.com',
protocol: 'https:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kEndCalled)]: true,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(errored)]: null,
[Symbol(kUniqueHeaders)]: null
},
data: {
status: 400,
err: 'Render timeout, try requesting fewer or smaller images'
}
},
isAxiosError: true,
toJSON: [Function: toJSON]
}
Of which right at the bottom it says:
data: { status: 400, err: 'Render timeout, try requesting fewer or smaller images' }
This is strange because this API endpoint does not mention anything about images and there is a specific endpoint that is called GET image which would surely be for images.
My question is: Is there a way to get the JSON for the large file? I think the answer is no (not easily anyway as I would probably have to get nodes in chunks and then piece the JSON back together again -- haven't tried this).
I have only tried the GET file endpoint and expected it to give me the JSON of the Figma file.
-
Have you gathered any additional information about this? I've just run into this problem today.borrimorri– borrimorri2023年05月25日 15:44:07 +00:00Commented May 25, 2023 at 15:44
-
1@borrimorri I have opened an issue on Github for it github.com/figma/figma-api-demo/issues/46. Other than that I believe that there is no current way around this.Edouard Finet– Edouard Finet2023年06月05日 16:12:34 +00:00Commented Jun 5, 2023 at 16:12
1 Answer 1
Why this happens
Even though the GET /v1/files/{file_key} endpoint is designed to return a JSON tree, Figma internally still processes the full file structure. For large or complex files, this can trigger errors.
Workaround
To avoid this issue, you can reduce the load by only fetching the top-level nodes first, then fetching child nodes separately. Steps:
Call the endpoint with
depth=1to get only the shallow structure.Extract the node IDs of the children you need.
Use the
idsquery parameter to request only those specific nodes.Merge the results manually. That means injecting the fetched children back into the shallow structure.
Retrofit Java Example
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface FigmaRestApi {
// Step 1: Fetch the file structure with minimal depth.
// This returns only the top-level node tree (depth = 1) to avoid timeouts on large files.
@GET("/v1/files/{file_key}")
Call<FigmaFile> getFigmaFile(
@Path("file_key") String fileKey,
@Query("depth") int depth // Should be set to 1
);
// Step 2: Fetch specific node(s) by ID.
// The 'ids' parameter accepts a comma-separated list of node IDs.
// In our use case, we pass a single node ID at a time in a loop to simplify merging and avoid large payloads.
@GET("/v1/files/{file_key}")
Call<FigmaFile> getFigmaFileWithIds(
@Path("file_key") String fileKey,
@Query("ids") String ids // Single node ID per request, e.g., "12:3"
);
}