Skip to main content
This guide will help you understand how to migrate from Private API v1 to API v2. You can also share the prompt below with your AI agent to make the migration smoother.

Migrate this project's Nonce API v1 integration to API v2

The resulting code still needs review. This guide covers moving Private API v1 read integrations to Public API v2. Resource paths, query scope, pagination, and some response fields change. Valid workspace API keys, Bearer authentication, and the outer success, data, and error response fields stay the same.
Private API v1 is a legacy interface and will be retired later. In v2, the workspace, classification metrics, and historical metrics endpoints are beta and may still receive breaking changes.
Examples use https://api.nonce.app as the base URL. The v1 paths below omit the /private-api/v1/{workspace_id} prefix; /me is the root path /private-api/v1/me.

Authentication

Valid workspace API keys continue to work with Authorization: Bearer <workspace_api_key>. In v2, 401 indicates an authentication failure, 403 indicates a permission failure, and 404 indicates an inaccessible or missing scoped resource, such as a farm outside the key’s scope. None of these responses represent an empty list.

Pagination

Lists paginate through query parameters and searches through the JSON request body. Both use page and page_size. Both parameters are optional and default to page=1 and page_size=20. Historical metrics do not paginate; they are queried by time window. Pagination metadata is returned in pagination, alongside success, data, and error. Read from page 1 and request the next page while page < total_pages; stop when total is 0. Data can change between requests, so results across pages are not a point-in-time snapshot. The v1 GET endpoints did not define limit or offset. Integrations that sent those parameters need to check whether their original pagination worked as intended.

Workspaces

Integrations that already hold a workspace_id keep using it. Integrations that resolved workspace details through /me use ListWorkspaces instead:
data is an unpaginated array, and the target workspace is selected by id. On each item, workspace_id, workspace_name, and workspace_slug become id, name, and slug. With an API key, the list contains the key’s own workspace (relations includes member) and workspaces with valid grants (relations includes grantee). With OAuth, it contains the workspaces the current user has joined. Each item also carries a permissions field, which describes effective permissions only. ListFarms lists the farms accessible with the same credentials. The permissions values in ListWorkspaces describe allowed API actions, not a list of farm IDs. No v2 endpoint currently returns the full farm_ids and grant-expiration record from /me/grants; integrations that depend on those details need a confirmed data source before migration can be completed.

Farms

v1:
v2:
v2 includes archived farms that have not been deleted; filtering out archived: true restores the v1 scope. The sort order also changes, so a v1 page number cannot be relied on to identify the same records in v2.

Agents

v1 listed Agents across the whole workspace:
v2 lists them per farm:
A workspace-wide view iterates over the accessible farms and pages through each farm’s Agents; the workspace count is the sum of total across the farms in scope. List items provide status, version, uptime, and update time. The nested farm object is replaced by farm_id. Host details are not part of the list summary; see the compatibility notes.

Miners

ListMiners returns unfiltered lists; SearchMiners applies filters and returns the same summary objects.
Units are unchanged: hashrate and expected_hashrate in H/s, power in W, efficiency in J/TH. Summaries do not include every field of the v1 full object. Integrations that read errors, hashboards, pools, psus, hashrate_24h, or reboot counts need a separate data-access solution. The compatibility notes describe these limitations; renaming fields alone is not enough.

Filters

SearchMiners takes filters in a JSON body. Top-level conditions are combined with AND; for example, “online with a fan or power anomaly”:
A condition that combines fields with OR, such as “stale or anomalous”, requires two queries whose results are merged by miner ID. anomaly_filters becomes anomaly_flags.in; ip_ranges remains an array. status accepts only online and stale. v1 converted offline to stale and ignored deprecated health values such as error and low_hashrate. A migrated request reproduces that behavior: offline becomes stale, and the ignored values are dropped rather than converted into an anomaly_flags condition, which would filter results that v1 did not. The compatibility notes explain this in detail. For an exact MAC lookup, set search.field to "mac" and provide the complete address in search.value. Field search matches substrings, so read all candidate pages, compare full addresses case-insensitively, and count only exact matches. The gt and lt conditions on last_updated_at are strict; records equal to either boundary are excluded.

Classification metrics

QueryFarmMinerMetrics (beta) returns a snapshot of current miner classifications and distributions. The request changes from GET to POST and carries no body. v1:
v2:
theo and total are unchanged. period is new and gives the observation time, and mining_modes entries gain firmwares. Classifications can overlap, so the overall count comes from total rather than from summing categories. The endpoint reports live state, so counts vary between calls. For a farm that does not exist, v1 could return empty stats; v2 returns 404. Time series come from historical metrics.

Historical metrics

QueryFarmMetrics (beta) replaces paged history with time-window queries: one request returns the buckets for the requested window. For daily metrics on 2026-09-01 UTC: v1:
v2:
The window is half-open: from_time is included and to_time is excluded, so a full end date ends at midnight of the following day. For non-UTC dates, the boundaries are built in the business time zone and then converted to ISO timestamps. granularity should always be sent explicitly. v1 defaulted to day; v2 defaults to hour.

Response

The record array moves from data to data.snapshots, and data also carries the window information. Daily buckets keep the pool, agent, finance, and electricity field groups. snapshots are in ascending order. Buckets with no data are present and contain null metrics rather than 0. Because empty buckets are present, the latest N buckets and the latest N buckets with data are different selections. Snapshots no longer include id or workspace_id; farm, granularity, and period identify a point. period is the start of the metric bucket, expressed as an ISO 8601 string.

Range limits

A single query covers at most: Longer ranges are split at bucket boundaries into adjacent, non-overlapping windows and merged by period, keeping the original time-zone meaning. A v1 request for 7 days at 10min or 60 days at hour therefore becomes several v2 queries.

Task batches

Lists use query parameters; searches use a JSON body.
batch_id becomes id. In list items, failed_count becomes unsuccessful_count and still counts failures, timeouts, and cancellations together.

Detail and tasks

v1 embedded tasks in the task batch detail. v2 splits them: GetTaskBatch returns the summary, and ListTaskBatchTasks returns the tasks as a paginated list.
In the detail response, failed_count counts execution failures only. It is not the same figure as the list’s unsuccessful_count.