This site contains affiliate links — we may earn a commission if you sign up through them.

DigitalOcean API v2 Guide — Automation for Developers

คู่มือสำหรับการเรียก DigitalOcean API v2 ในการสร้าง Droplet, DNS และ Firewall โดยอัตโนมัติผ่านสคริปต์

DigitalOcean API v2 Guide — Automation for Developers

A practical developer's guide to the DigitalOcean API v2 — authentication, curl examples for Droplets, DNS, and Firewalls, rate limiting, and automation scripting.

REST API v2 Overview

DigitalOcean API v2 is a standard REST API that communicates over HTTP using JSON format for both request and response. It has a base URL of https://api.digitalocean.com/v2/ and separates resources by feature, such as /v2/droplets, /v2/domains, /v2/databases, /v2/kubernetes/clusters, /v2/load_balancers, and /v2/firewalls. This structure makes nearly every feature visible in the Control Panel callable through code without needing to log in to the web interface manually. Authentication uses a Bearer token included in the HTTP header Authorization: Bearer $TOKEN with every request (details on creating tokens are in the next section). HTTP methods follow standard REST conventions: GET for retrieving data, POST for creating new resources, PUT/PATCH for updates, and DELETE for removal. For example, retrieving all Droplets in an account can be done with curl -X GET -H "Authorization: Bearer $TOKEN" "https://api.digitalocean.com/v2/droplets". The response returned is JSON wrapped with a resource key such as droplets, along with object links and meta showing the total count. For lists with many items, the response includes links.pages object providing URLs for the next and previous pages, eliminating the need to calculate offsets manually. For those who prefer not to write HTTP requests by hand every time, DigitalOcean provides official client libraries for multiple languages: godo for Go and pydo for Python, which wrap the API v2 into ready-to-use functions. There's also an official CLI tool called doctl that serves as the command-line interface for this API without writing curl commands yourself—ideal for frequently run tasks or shell scripts. The complete API reference documentation is available at developers.digitalocean.com, which should be kept open alongside this article at all times, as each resource has its own specific fields. For instance, Droplets require size, image, and region specifications, while Load Balancers require forwarding_rules, and so on. Understanding this basic structure first helps you read the code examples in the following sections much faster, because every endpoint follows the same pattern: headers, JSON body, and HTTP method according to REST standards.

Creating a Personal Access Token

From our hands-on testing — before calling the API, you must create a Personal Access Token first. This is generated from the Control Panel at cloud.digitalocean.com/account/api/tokens by clicking the Generate New Token button. Give the token a meaningful name, such as specifying which script or server uses it, then select scope: either read-only or full access (read and write). If the script only retrieves and displays data, choose read-only to minimize risk if the token is compromised. If you need to create or delete resources, you must select write as well. DigitalOcean allows you to set an expiration date for the token at creation time—such as 30 days, 90 days, or no expiration. For tokens used in long-term automation scripts, setting an expiration date is recommended along with a calendar reminder to create a new one before it expires, preventing your automation system from suddenly stopping from an expired token without notice. When you click Generate, the system displays the token value only once. Copy and save it immediately, as it cannot be viewed again after closing the page—you must create a new token instead. The recommended way to store it is in an environment variable rather than hardcoding it directly into source files, such as export DIGITALOCEAN_TOKEN="dop_v1_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" and then reference $DIGITALOCEAN_TOKEN in scripts or CI/CD pipelines instead. This approach prevents accidentally committing the token to a Git repository, which is the most common cause of token leaks. If using Git, always add the file containing the token to .gitignore. If you discover a token has been compromised by any means, immediately go to the API Tokens page and click Revoke, because an unexpired, unrevoked token can be used to call the API with full permissions like an account password. Test that the token works correctly by making a simple request to the account endpoint, such as curl -X GET -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "https://api.digitalocean.com/v2/account". If the token is valid, you'll get JSON back with account details like email and authentication status, but if the token is incorrect or expired, you'll receive HTTP status 401 with an error message instead.

curl Examples: Creating and Deleting Droplets

Creating a Droplet via the API requires sending a POST request to /v2/droplets with a JSON body specifying at least 4 values: name (the droplet name), region (such as sgp1 for Singapore, the closest region to Thailand users), size (size slug such as s-1vcpu-1gb, matching the Basic Droplet plan with 1 GiB RAM/1 vCPU at $6/month), and image (such as ubuntu-24-04-x64). A complete example command: curl -X POST -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"name":"web-01","region":"sgp1","size":"s-1vcpu-1gb","image":"ubuntu-24-04-x64"}' "https://api.digitalocean.com/v2/droplets". Upon success, you receive HTTP status 202 Accepted with JSON containing the new Droplet with status new and an action id currently running. Since Droplet creation is not instantaneous, you must poll the /v2/actions/$ACTION_ID endpoint until the status field changes to completed, then call GET /v2/droplets/$DROPLET_ID to retrieve the actual assigned IP address for the next step. Viewing all Droplets in the account is done with GET /v2/droplets as mentioned in the first section. Deleting a Droplet uses DELETE with the Droplet id appended to the URL, such as curl -X DELETE -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "https://api.digitalocean.com/v2/droplets/12345678". A successful delete returns HTTP status 204 No Content (no response body), meaning the Droplet is permanently destroyed. All data on the disk is immediately lost if you didn't create a Snapshot beforehand, so always verify you have the correct id before issuing DELETE, especially in automation scripts that delete multiple Droplets. You should include a dry-run step or log the list of ids to be deleted for inspection before executing the actual delete. Beyond simple deletion, other actions are available through POST /v2/droplets/$ID/actions, such as power_off, reboot, resize, or snapshot, all of which return an object with an id to track status the same way as during Droplet creation.

Key takeaway: Create Droplet with POST /v2/droplets specifying name, region, size (e.g., s-1vcpu-1gb = $6/month), image

Managing DNS, Snapshots, and Firewalls via API

Beyond Droplets, API v2 also covers three additional features commonly used in automation: DNS, Snapshots, and Firewall. On the DNS side, add a domain to the system with POST /v2/domains specifying the domain name and an initial A record IP address, then manage additional records at /v2/domains/$DOMAIN/records. For example, adding a CNAME record: curl -X POST -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"type":"CNAME","name":"www","data":"@","ttl":3600}' "https://api.digitalocean.com/v2/domains/example.com/records". This is especially useful for automatically creating many subdomains, such as a system that provisions new Droplets and then immediately assigns subdomains to customers via automation without manually visiting the Control Panel one by one. For Snapshots, create them from a Droplet action as mentioned earlier via POST /v2/droplets/$ID/actions with body {"type":"snapshot","name":"backup-2026-07-17"}, ideal for setting up cron jobs for automated daily backups. Be aware that Snapshots incur storage costs at $0.06 per GiB per month. If you set up a script to create snapshots daily without deleting old ones, storage charges will accumulate continuously. Thus, you should write the script to also delete snapshots older than a certain threshold, such as keeping only the last 7 days, using DELETE /v2/snapshots/$SNAPSHOT_ID on older snapshots. For Firewall (Cloud Firewall, which has no additional cost), create one with POST /v2/firewalls specifying inbound_rules, outbound_rules, and droplet_ids to attach this firewall to. An example of rules allowing only ports 22 (SSH) and 443 (HTTPS): curl -X POST -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"name":"web-fw","inbound_rules":[{"protocol":"tcp","ports":"22","sources":{"addresses":["0.0.0.0/0"]}},{"protocol":"tcp","ports":"443","sources":{"addresses":["0.0.0.0/0"]}}],"droplet_ids":[12345678]}' "https://api.digitalocean.com/v2/firewalls". Creating Firewalls via API allows you to define standard security policies as code and attach them to every new Droplet provisioned through the same script, reducing the risk of forgetting Firewall configuration when manually creating new servers.

  1. Add domain with POST /v2/domains then manage records at /v2/domains/$DOMAIN/records
  2. Create Snapshot via Droplet action type snapshot at a cost of $0.06/GiB/month; remember to delete old unused ones via script
  3. Delete old Snapshots with DELETE /v2/snapshots/$ID to control costs
  4. Create Firewall with POST /v2/firewalls specifying inbound/outbound rules and droplet_ids (no additional cost)
  5. Attach standard Firewall to every new Droplet through the same script, reducing the risk of forgotten configuration

Rate Limiting and Error Handling

API v2 enforces rate limiting to prevent any single user from making requests frequently enough to impact the shared infrastructure. To check your remaining quota, don't guess—every API response includes HTTP headers reporting the current status: RateLimit-Limit (maximum quota in this period), RateLimit-Remaining (requests left), and RateLimit-Reset (Unix timestamp when quota resets). Scripts making many requests should read these headers on every response and slow down when RateLimit-Remaining approaches zero, rather than making rapid-fire requests until being blocked. Since the actual limit may vary by token type or endpoint, do not hardcode fixed numbers into your code; instead read the header values every time. An example of checking headers with curl: curl -I -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "https://api.digitalocean.com/v2/droplets" uses the -I flag to fetch only headers without the full body, suitable for checking quota lightly before making real requests. When quota is truly exhausted, the API responds with HTTP status 429 Too Many Requests, which your script should catch and retry after a delay (not immediately) rather than continuing to hammer the endpoint. A popular retry pattern is exponential backoff: wait 1 second, then 2 seconds, then 4 seconds after each failed retry, up to a configured ceiling. Other common errors include 401 Unauthorized (token wrong or expired), 404 Not Found (resource id doesn't exist or is misspelled), 422 Unprocessable Entity (sent malformed data fields, such as an invalid size slug), and 500/503 (server-side issues that may be retried). All error responses contain a JSON body in a consistent format: {"id": "not_found", "message": "The resource you were accessing could not be found."} which your script should parse and log the message field every time, not just the HTTP status code, because the message often clarifies exactly what went wrong—such as which field was sent incorrectly—making debugging much faster. This is especially important for unattended scripts running on cron or CI without a person watching the logs in real-time.

Using the API with Automation Scripts

Once you understand the main endpoints and error handling, the next step is to combine everything into a reusable script that runs without manual intervention. The simplest example is a bash script using curl combined with jq (a command-line JSON parser) to create a Droplet, wait for it to be ready, and print its IP address: DROPLET_ID=$(curl -s -X POST -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"name":"web-01","region":"sgp1","size":"s-1vcpu-1gb","image":"ubuntu-24-04-x64"}' "https://api.digitalocean.com/v2/droplets" | jq -r '.droplet.id'). Then use a while loop to repeatedly check status via GET /v2/droplets/$DROPLET_ID until the status field becomes active, then extract the IP with jq from .droplet.networks.v4[0].ip_address to use in the next automation step, such as automatically adding a DNS record pointing to that IP. For scheduled tasks like daily backups, use cron to call a script that creates a Snapshot and then deletes old Snapshots as described earlier, with an entry like 0 3 * * * /home/user/scripts/do-backup.sh running at 3 AM daily. If your team already uses Python, write the same logic using the requests library instead of curl, or use the official pydo library which wraps endpoints into ready-made functions, reducing the chance of typos in URLs or field names. Another alternative is doctl, DigitalOcean's official CLI, which wraps API v2 already; install it via brew on macOS or snap on Linux, authenticate with doctl auth init using the same Personal Access Token, and then commands like doctl compute droplet create web-01 --region sgp1 --size s-1vcpu-1gb --image ubuntu-24-04-x64 do the same thing as the curl example but much shorter, suitable for scripts emphasizing readability. For CI/CD systems like GitHub Actions, store the token as an encrypted repository secret, then reference it in your workflow file via ${{ secrets.DIGITALOCEAN_TOKEN }} to allow the pipeline to call the API or doctl without exposing the token in code or logs, turning provisioning and deployment into fully automated pipeline steps without anyone manually running commands one by one.

Common Mistakes and Fixes

A point users often miss: when using API v2 in production scripts that run repeatedly or integrate with other systems, certain mistakes recur. The most frequent is mishandling 401 Unauthorized. Many teams write scripts that crash immediately on 401 without distinguishing whether the root cause is an expired token or simply a misconfigured environment variable on a new machine. A better approach is logging the full response body (which includes a message field explaining the cause) rather than only checking status code, so you can tell whether the issue is token rotation or just configuration. Another common mistake is mishandling 422 Unprocessable Entity, which results from sending malformed data—such as a typo in the size slug (e.g., s-1vcpu-1g instead of s-1vcpu-1gb) or a nonexistent region. Many scripts retry the identical failed request over and over on 422, which will never succeed because the problem is in the request itself, not a temporary server issue. You should clearly separate retriable errors (429, 500, 503) from non-retriable ones (400, 401, 404, 422), letting the non-retriable group fail immediately with detailed logging instead of wasting time retrying. A pagination bug is another frequent problem: scripts fetching large lists of Droplets or records forget to check links.pages.next on every page, so they retrieve only the first page at the default per_page limit and mistakenly think they have everything. Fix this by looping through links.pages.next until there is no next key, or specify per_page higher from the start in scripts you know will have many results, such as curl -X GET -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "https://api.digitalocean.com/v2/droplets?per_page=200". Finally, an idempotency issue: endpoints like POST /v2/droplets lack built-in idempotency keys. If your script makes the same POST again due to network timeout or retry logic without checking first, you might create duplicate Droplets unintentionally. Workaround: before retrying resource creation, GET and check whether it already exists by name or other identifier, or add idempotency logic at the application level. More dangerous is a retry-storm on 429: if multiple scripts all hit rate limit at once and then retry immediately without backoff or jitter, they compound the problem further. Always include a random delay (jitter) in retry waits, not just the same fixed interval for all retries, to spread them out rather than letting them bunch together.

Best Practices

When running API v2 in long-term production systems, several concrete practices reduce problems and risk. Start with token scoping: create separate tokens by job function instead of using a single token everywhere. For example, a backup script's token should have only the scope it needs, never permission to delete Droplets if that script never touches Droplets. This way, if any single token leaks, damage is limited to that token's scope, not the entire account. Combine this with periodic token rotation—create a new token every 90 days and revoke the old one, rather than using the same token for years without rotation. Next is webhook verification: if your external system receives callbacks when events occur (like Alert Policy notifications), the endpoint receiving webhooks must verify the request source before trusting the payload—check source IP or validate a signature header. Never trust webhook data without some form of authentication, as unguarded webhook endpoints are frequent attack targets via request spoofing. On retry logic, use exponential backoff with jitter as a shared function called by every script, not rewritten separately in each place. For example, write it once: retry_with_backoff() { local attempt=0; local max=5; until curl -sf -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "$1"; do attempt=$((attempt+1)); [ $attempt -ge $max ] && return 1; sleep $((2**attempt)); done; } so all scripts share consistent retry behavior without duplicating logic, and you can fix issues in one place. Finally, logging and observability: unattended automation scripts on cron or CI should log every API call at minimum with timestamp, endpoint called, HTTP status received, and involved resource ids (such as droplet id). Keep logs in a separate file, not just stdout, because when something later goes wrong—like a Droplet vanishing unexpectedly or Snapshots not created on schedule—you can trace backward through logs to see what API calls the script actually made at that time. For systems with many automation scripts, consider sending logs to a centralized logging system for easier cross-script event inspection.

Get $200 Free Credit →

Frequently Asked Questions

Is calling the API v2 free or does it have an extra cost?
The API calls themselves have no additional charge, but resources created via the API—such as Droplets, Volumes, or Snapshots—are billed at the same rates as if you created them through the Control Panel.
Can a single token access every resource type in an account?
Yes, a read/write scoped token has the same permissions as the user who created it across all account resources. You should set scope and expiration dates appropriately for each script's function, and avoid using the same token everywhere unnecessarily.
Can I manage Spaces (Object Storage) via API v2?
File upload/download to Spaces uses a separate S3-compatible API, not API v2 endpoints directly. However, you can create access keys for Spaces via API v2, but file operations require an S3 client library such as boto3 or AWS CLI pointing to DigitalOcean's endpoint.
Should I use API v2 directly, or doctl, or Terraform instead?
It depends on the task. API v2 is best for custom logic that doctl doesn't support; doctl suits general scripting work for speed and readability; Terraform suits declarative infrastructure management with version-tracked state changes. All three call API v2 under the hood—choose based on team preference.
How dangerous is it if I forget to delete a token I no longer use?
An unexpired, unrevoked token retains full permissions per its scope indefinitely. Anyone with access to that token can create, delete, or modify resources in your account. You should periodically review and revoke unused tokens.
My script keeps getting 429. What should I do?
Read the RateLimit-Remaining header before each request and throttle when it approaches zero. Also add exponential backoff retry logic on 429, and consider batching multiple requests into one if the endpoint supports it, to reduce total call volume.