Most Moz API methods return their data directly in the response to your request.
Some methods instead do their work in the background: they immediately hand back
a receipt called an async_call, and you collect the data shortly afterwards by
looking that receipt up. This guide explains why those methods exist, what an
async_call contains, and how to poll one until it produces a result.
Why Some Methods Are Asynchronous
A few of the operations exposed by the Moz API take long enough that holding an
HTTP connection open until they finish would be unreliable. Rather than risk
proxy and client timeouts on a request that is still perfectly healthy, these
methods accept your request, acknowledge it, and do the work outside of the
request that started it.
The trade-off is that getting data from an asynchronous method takes at least
two requests instead of one. The method documentation states whether a method is
asynchronous.
The Async Call Object
Every asynchronous method returns the same envelope, whatever the method does
and whether or not the work eventually succeeds:
id: The identifier for this call. Pass it to async.call.lookup to check on
the request and collect its data.
action: The name of the method that is being run.
status: How far along the request is, described below.
ttl: The number of seconds remaining before this call is discarded.
result: The data the method produced, or null if it has not finished.
error: The error the method produced, or null if it has not failed.
An asynchronous method always returns an async_call. In some instances an
asynchronous method could return an async_call that is already complete on
the first call.
Call Statuses
A call progresses through the following statuses:
pending: The request is queued and waiting for relevant data.
processing: The result is being generated.
complete: The request has finished, successfully or otherwise.
While the status is anything other than complete, both result and error
will be null and you should keep polling. Once the status is complete, exactly
one of result or error will be non-null.
The Request Lifecycle
The examples below use test.ping.async, a method that exists purely so you can
exercise this flow. It costs no quota, and its completion is deliberately held
back for about five seconds. The delay is provided so that your code can test
verifying a call moving from pending to complete status on subsequent polls.
First, call the asynchronous method as you would any other Moz API method:
Once the work is done, the same lookup returns the finished call. This is the
response you act on.
Polling for a Result
Repeat the async.call.lookup request until the status is complete. We
generally suggest not polling more than once every five seconds; the
documentation for an individual method may suggest a longer interval if that
method typically takes longer than that to run. Polling more aggressively will
not return your data any sooner, and may result in your requests being rate
limited.
async.call.lookup consumes no quota, so polling costs you nothing beyond the
request itself. The quota for the underlying work is accounted for by the
asynchronous method that started the call, not by the lookups.
Successful Requests
A request that succeeded has a status of complete, a populated result, and a
null error:
The contents of async_call.result are exactly what the method would have
returned in the top-level result had it been synchronous, so the response
format documented for the method describes async_call.result verbatim.
Failed Requests
There are two distinct ways an asynchronous request can fail, and they look
different.
The first is a request that is rejected before it is ever accepted: an invalid
token, a malformed parameter, or insufficient quota. These fail exactly like a
synchronous request: you get the appropriate HTTP status code and a top-level
error, with no async_call at all. The
error handling guide covers
these.
The second is a request that was accepted and then failed while being worked on.
The lookup succeeds, so the HTTP status is 200 and there is no top-level
error. The failure is reported inside the call, with a status of complete, a
null result, and a populated error:
{"id":"01a045a4-7b13-7d90-ae52-3c1f8b0d7264","jsonrpc":"2.0","result":{"async_call":{"id":"18e5de71-af54-490d-8eb1-0596a4f68121","action":"test.ping.async","status":"complete","ttl":20,"result":null,"error":{"code":-32653,"status":400,"data":{},"message":"We've thrown an error!"}}}}
async_call.error has the same shape as a top-level error object, and its
code and status describe the failure exactly as they would have for a
synchronous request, so you can hand it to the same error handling code. Because
the HTTP status of the lookup reflects the lookup rather than the work, check
async_call.error rather than the HTTP status to decide whether the request
succeeded.
Call Time to Live
The ttl on a call is the number of seconds remaining before that call is
discarded and its data is no longer retrievable. It counts down as the call
ages, so the value drops between polls.
While a call is in progress, the ttl reflects how long the request will be
retained while it is being worked on. This defaults to 120 seconds for most
asynchronous methods. Read the value from the response rather than assuming a
fixed number, as a method expected to take longer may be given a longer window.
Once you fetch a call whose status is complete, its TTL is set to 20 seconds.
That short window exists only so that you can retry the lookup if the response
is lost in transit; it is not a cache to read from later. When you receive a
complete call, use that response’s result or error immediately rather than
looking the call up again later.
If a call’s TTL reaches zero it is discarded, and any further lookup of that ID
returns a Call Not Found error.
Call Not Found Errors
async.call.lookup returns a Call Not Found error when the ID you asked for
does not correspond to a call that is still being retained. It comes back with a
404 status and a -32655 code:
{"id":"01a045b0-c8d5-7a36-8e14-6f92b7c3ad08","jsonrpc":"2.0","error":{"code":-32655,"status":404,"data":{},"message":"Call was not found."}}
The common causes are:
More than 20 seconds have passed since you first retrieved the completed call.
The call expired before you collected it, because polling stopped or was never
started.
A Call Not Found error is not a transient condition and retrying the lookup will
not help; the call and any data it held are gone. To recover, call the
asynchronous method again to start a new request.
Testing Your Integration
Two methods exist specifically so you can verify your handling of this flow
without spending quota or interpreting real data:
Async Ping
(test.ping.async) returns an async_call that stays pending for about
five seconds before completing, echoing back the message you sent. Pass
error: true instead to have the call complete with an error, which lets
you confirm that your client distinguishes a failed asynchronous request from
a successful one.
Ping (test.ping) is the
synchronous equivalent, useful for checking your endpoint, headers, and token
in isolation before adding polling.
Both methods cost zero rows, but they do require an account with Moz API access.