Call the Cloud Platform API
Goal: make one authenticated call to the Cloud Platform API: the REST API behind everything acli and the Cloud Platform user interface can do.
Reach for it when a task has to run without a person at the terminal (a nightly database refresh, environment creation in a pipeline) or when no acli command covers what you need. For everything else, acli wraps this API and handles the token exchange for you: start at the CLI quickstart.
What you’ll have when you’re done
Section titled “What you’ll have when you’re done”- An API token (API Key + API Secret) for your Acquia account.
- A working OAuth 2.0 access token and one successful
curlcall listing your applications. - The two failure modes you’ll actually hit (expiry and rate limiting) and their fixes.
Prerequisites
Section titled “Prerequisites”- An Acquia account that can sign in to cloud.acquia.com
curl(preinstalled on macOS and Linux)- Optional:
jq, the standard JSON filter. Step 4 uses it to pull the token out of the exchange response, and states how to manage without it.
-
Generate an API token
Section titled “Generate an API token”Open cloud.acquia.com/a/profile/tokens, give the token a human-readable label (for example
local-automation), and clickCreate Token.Record the
API KeyandAPI Secretnow: you can’t retrieve them after closing the browser tab. -
Put the credentials in a
Section titled “Put the credentials in a .env file”.envfile.env ACQUIA_CLOUD_API_KEY=d2b18a6c-0000-0000-0000-000000000000ACQUIA_CLOUD_API_SECRET=your-api-secretThese are deliberately not named
DRUPAL_CLIENT_ID/DRUPAL_CLIENT_SECRET. That pair names a per-site API client for the Content API; this key/secret pair is an account-level Cloud Platform credential that inherits your user’s permissions across the whole subscription. Distinct names keep the two from ever being swapped in a script. -
Exchange the credentials for an access token
Section titled “Exchange the credentials for an access token”The Cloud Platform API uses the OAuth 2.0
client_credentialsgrant against Acquia’s central accounts service. Use--data-urlencode(not--data) so non-alphanumeric characters in the secret survive encoding:Terminal window set -a; source .env; set +acurl -s https://accounts.acquia.com/api/auth/oauth/token \--data-urlencode "client_id=$ACQUIA_CLOUD_API_KEY" \--data-urlencode "client_secret=$ACQUIA_CLOUD_API_SECRET" \--data-urlencode "grant_type=client_credentials"A successful response looks like this:
{"token_type": "Bearer","expires_in": 300,"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9…(redacted)","scope": "role:authenticated-user"}Note
expires_in: 300: the token lives 300 seconds (5 minutes). Scripts regenerate it rather than store it. -
Make one authenticated call
Section titled “Make one authenticated call”Capture the token and list the applications your account can see:
Terminal window TOKEN=$(curl -s \https://accounts.acquia.com/api/auth/oauth/token \--data-urlencode "client_id=$ACQUIA_CLOUD_API_KEY" \--data-urlencode "client_secret=$ACQUIA_CLOUD_API_SECRET" \--data-urlencode "grant_type=client_credentials" \| jq -r .access_token)curl -s https://cloud.acquia.com/api/applications \-H "Authorization: Bearer $TOKEN"Without
jq, run the token request from step 3 on its own and paste theaccess_tokenvalue intoTOKEN=yourself.A
200response returns JSON listing your applications (trimmed):{"total": 1,"_embedded": {"items": [{"uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479","name": "myproject","subscription": {"uuid": "…(redacted)","name": "MyProject"}}]},"_links": {"self": {"href": "https://cloud.acquia.com/api/applications"}}}That application
uuidis the handle every other Cloud Platform API endpoint takes: environments, deployments, backups, logs, cron, and more.
What just happened
Section titled “What just happened”Acquia’s accounts service exchanged your account-level API Key and Secret for an access token via the OAuth 2.0 client_credentials grant, and the Cloud Platform API at cloud.acquia.com/api accepted that token in an Authorization: Bearer header. The token expires after 300 seconds, so real automation wraps the exchange in a helper and re-runs it per batch of calls. The API is rate limited to 100 calls per minute per user, enough for CI tasks, but worth knowing before you write a polling loop.
When something goes wrong
Section titled “When something goes wrong”{ "error": "invalid_client", "error_description": "The client secret supplied for a confidential client is invalid." }
either the key/secret is wrong, or the token request wasn’t form-encoded. The token endpoint only accepts Content-Type: application/x-www-form-urlencoded (which --data-urlencode sets); re-check the credentials from cloud.acquia.com/a/profile/tokens and keep the request shape from step 3. If the secret is lost, create a new token; secrets are shown once.
{ "errorCode": "E0000021", "errorSummary": "Bad request. Accept and/or Content-Type headers likely do not match supported values." }
you sent the token request as JSON. The OAuth 2.0 framework doesn’t support application/json here; send the fields form-encoded as in step 3.
403 Forbidden with { "error": "unauthorized", "message": "The access token has expired." } on the API call itself
the access token expired; it only lives 300 seconds. Re-run the token exchange and retry. (A malformed or revoked token returns the same 403 with "The access token provided is invalid." instead.)
HTTP/1.1 429 Too Many Requests
you exceeded the 100-calls-per-minute rate limit. The response carries a Retry-After header with the number of seconds until calls are unblocked:
HTTP/1.1 429 Too Many RequestsRetry-After: 57The fix: back off, wait the Retry-After seconds, then retry, and add that backoff to any loop that made this happen.
Next steps
Section titled “Next steps”- Cloud Platform API reference (cloudapi-docs.acquia.com): the canonical OpenAPI docs for all 200+ endpoints.
- Acquia APIs directory: every other Acquia API, and where each is documented.
- Everyday CLI workflows: the
aclicommands that wrap these endpoints, for the tasks that don’t need rawcurl.
Was this page helpful?
What went wrong?
Still stuck? Contact Acquia Support (opens in a new tab)