# Get all finance metrics

> Eto API reference for `GET /api/v1/finance/{shop_id}`. Base URL `https://eto.tools/api/v1/`. Authenticate with the `X-Eto-API-Key` header.

Part of the [Eto API reference](https://eto.tools/dev/docs/). The full reference in one file is at [llms-full.txt](https://eto.tools/dev/docs/llms-full.txt); the machine-readable schema is at [openapi.json](https://eto.tools/dev/docs/openapi.json).

## GET /api/v1/finance/{shop_id}

Get all finance metrics

- Operation ID: `finance`
- Slug: `finance`
- Category: Finance
- Authentication: API key only. Reads or writes data Eto holds for your own account.
- HTML: https://eto.tools/dev/docs/#ep-finance
- Markdown: https://eto.tools/dev/docs/finance/finance.md

Returns every financial metric for a store over a date range. Zero Etsy API calls — reads directly from the Eto database. Instant.

━━━ WHAT YOU GET BACK (always returned) ━━━

shop_id — the store
shop_name — store display name
currency — e.g. "GBP", "USD"
start / end — the range you requested
balance_cents — current store balance (latest ledger entry, not range-scoped)
paid_out_cents — total deposited to bank in this range
sales_count — number of sales
gross_sales_cents — raw sale revenue before any deductions
sales_cents — display sales value (gross - refunds - cancelled + remitted tax)
fees_cents — total Etsy fees charged (negative number)
ads_cents — total ad spend (negative number)
refund_cents — total refunded to buyers
cancelled_cents — total cancelled order amounts
recoup_cents — Etsy recovering money from your balance
remitted_tax_cents — sales tax / VAT remitted by Etsy (negative)
gross_profit_cents — profit before COGS (gross + fees + ads + tax)
cogs_cents — cost of goods sold (from your item costs in Eto)
net_profit_cents — gross profit minus COGS

All amounts in cents. Divide by 100 for display. Negative = money out.

━━━ HOW TO PICK THE DATE WINDOW ━━━

EASIEST — pass `period`: today, yesterday, last_7_days, last_30_days, this_month, last_month.
  The server resolves it in the shop timezone (override with `timezone`). No timestamps, no math.
Specific days — pass `start_date` / `end_date` as "YYYY-MM-DD" (inclusive).
Advanced — pass raw `start` / `end` unix seconds (range is [start, end), max 366 days).
The response echoes back a `range` object (exact start/end unix, start_local/end_local ISO,
timezone, label) AND the current server time — so you never compute or guess dates, and never
need a separate "what time is it" call. If the user means THEIR calendar day, pass their timezone.
Any store — use any shop_id connected to your Eto account (GET /api/v1/stores to list them).

━━━ BREAKDOWNS (optional) ━━━

Add &breakdown= to get detailed sub-breakdowns. Combine with commas.

breakdown=fees — returns breakdown.fees with:
  listing, listing_credits, transaction, transaction_credits,
  processing, processing_credits, shipping, shipping_credits,
  vat_on_fees, vat_on_fees_credits, regulatory, regulatory_credits,
  other, total

breakdown=ads — returns breakdown.ads with:
  etsy_ads, offsite_ads, offsite_ads_credits, subscription, total

breakdown=sales — returns breakdown.sales with:
  gross_sales_cents, refund_cents, cancelled_cents,
  remitted_tax: { sales_tax, sales_tax_credits, vat_ep, vat_ep_credits, total }

breakdown=gross — same as sales (alias)

breakdown=profit — returns both breakdown.gross_profit and breakdown.net_profit:
  gross_profit: { gross_sales_cents, fees_cents, ads_cents, remitted_tax_cents, total }
  net_profit: { gross_profit_cents, cogs_cents, total }

Combine: &breakdown=fees,ads,profit returns all three at once.
Omit entirely to get just the top-level numbers.

━━━ WHAT HAS BREAKDOWNS vs WHAT DOESN'T ━━━

HAS breakdown: fees, ads, sales/gross, profit
NO breakdown: balance_cents, paid_out_cents, sales_count, refund_cents,
  cancelled_cents, recoup_cents, cogs_cents, remitted_tax_cents
  (some of these appear inside the sales or profit breakdown instead)

━━━ HOURLY BREAKDOWN (optional, single-day only) ━━━

Add &hourly=true to get a per-hour breakdown alongside the day summary.
Only works when your range is 24 hours or less (single day).
If your range is longer than 24h, hourly returns null with a note.

What you get: an "hourly" array with one object per hour, from hour 0
(midnight) up to the hour containing your end time.

Each hour object contains:
  hour — 0-23 (the hour index)
  hour_start — unix timestamp of this hour's start
  hour_end — unix timestamp of this hour's end
  entry_count — number of ledger entries in this hour
  sales_count — number of sales
  gross_sales_cents — raw revenue for this hour
  sales_cents — display sales (gross - refunds - cancelled + tax)
  fees_cents — Etsy fees charged this hour
  ads_cents — ad spend this hour
  remitted_tax_cents — tax remitted this hour
  refund_cents — refunds this hour
  cancelled_cents — cancellations this hour
  recoup_cents — Etsy recoupments this hour
  gross_profit_cents — profit before COGS
  cogs_cents — cost of goods sold this hour
  net_profit_cents — profit after COGS
  paid_out_cents — payouts this hour

Combine with breakdowns: &hourly=true&breakdown=fees,ads works fine.
The hourly array gives per-hour summary metrics.
The breakdown object gives sub-category detail for the FULL day.

Turn it off: just omit &hourly (default is off). No penalty for not using it.
Multi-day range: hourly is ignored (returns null + hourly_note explaining why).

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `shop_id` | path | `integer` | yes | Your Etsy shop ID (from GET /api/v1/stores) |
| `period` | query | `string` | no | EASIEST — a named window the server resolves for you, no timestamps: today, yesterday, last_7_days, last_30_days, this_month, last_month. |
| `start_date` | query | `string` | no | Calendar day "YYYY-MM-DD" (inclusive). Use with end_date for a custom range. |
| `end_date` | query | `string` | no | Calendar day "YYYY-MM-DD" (inclusive). Defaults to start_date (single day). |
| `timezone` | query | `string` | no | IANA timezone for resolving period/dates (e.g. "Europe/London", "America/New_York"). Defaults to the shop's local timezone. Pass the USER's timezone (e.g. Europe/Helsinki) if they mean their own calendar day. |
| `start` | query | `integer` | no | Advanced: raw range start as unix seconds (inclusive). Prefer period/start_date. |
| `end` | query | `integer` | no | Advanced: raw range end as unix seconds (exclusive, max 366 days). Prefer period/start_date. |
| `breakdown` | query | `string` | no | Comma-separated breakdowns to include: fees, ads, sales, gross, profit. Omit for top-level numbers only. |
| `hourly` | query | `string` | no | Set to "true" to get per-hour metrics. Only works for single-day queries (range ≤ 24h). Returns array of 24 hour objects. Default: off. |

### Request

```bash
# ── All metrics for a period (no breakdown) ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1717113600"

# ── All metrics for a single day ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1716595200"

# ── All metrics + fee breakdown ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1717113600&breakdown=fees"

# ── All metrics + ads breakdown ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1717113600&breakdown=ads"

# ── All metrics + profit breakdown (shows gross + net) ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1717113600&breakdown=profit"

# ── All metrics + sales/tax breakdown ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1717113600&breakdown=sales"

# ── All metrics + EVERY breakdown combined ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1717113600&breakdown=fees,ads,sales,profit"

# ── Single day + fee and ads breakdown ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1716595200&breakdown=fees,ads"

# ── Single day + hourly breakdown (per-hour metrics) ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1716595200&hourly=true"

# ── Single day + hourly + fee breakdown (combine both) ──
curl -H "X-Eto-API-Key: eto_your_key" \
  "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1716595200&hourly=true&breakdown=fees"
```

```python
import requests

url = "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1717113600"
headers = {"X-Eto-API-Key": "eto_your_key"}

response = requests.get(url, headers=headers, timeout=120)
response.raise_for_status()
print(response.json())
```

```javascript
const url = "https://eto.tools/api/v1/finance/12345678?start=1716508800&end=1717113600";

const response = await fetch(url, {
  method: "GET",
  headers: {
    "X-Eto-API-Key": "eto_your_key",
  },
});
if (!response.ok) throw new Error(`Eto API ${response.status}`);
const data = await response.json();
```

### Response

```
// ── Response WITHOUT breakdown ──
{
  "shop_id": "12345678",
  "shop_name": "My Etsy Shop",
  "currency": "GBP",
  "start": 1716508800,
  "end": 1717113600,
  "balance_cents": 125430,
  "paid_out_cents": 90000,
  "sales_count": 15,
  "gross_sales_cents": 125000,
  "sales_cents": 120000,
  "fees_cents": -15000,
  "ads_cents": -5000,
  "refund_cents": 8000,
  "cancelled_cents": 2000,
  "recoup_cents": 0,
  "remitted_tax_cents": -3000,
  "gross_profit_cents": 102000,
  "cogs_cents": 30000,
  "net_profit_cents": 72000
}

// ── Response WITH breakdown=fees,ads,sales,profit ──
{
  "shop_id": "12345678",
  "shop_name": "My Etsy Shop",
  "currency": "GBP",
  "start": 1716508800,
  "end": 1717113600,
  "balance_cents": 125430,
  "paid_out_cents": 90000,
  "sales_count": 15,
  "gross_sales_cents": 125000,
  "sales_cents": 120000,
  "fees_cents": -15000,
  "ads_cents": -5000,
  "refund_cents": 8000,
  "cancelled_cents": 2000,
  "recoup_cents": 0,
  "remitted_tax_cents": -3000,
  "gross_profit_cents": 102000,
  "cogs_cents": 30000,
  "net_profit_cents": 72000,
  "breakdown": {
    "fees": {
      "listing": -400,
      "listing_credits": 40,
      "transaction": -8125,
      "transaction_credits": 325,
      "processing": -4800,
      "processing_credits": 192,
      "shipping": -650,
      "shipping_credits": 0,
      "vat_on_fees": -1200,
      "vat_on_fees_credits": 0,
      "regulatory": -500,
      "regulatory_credits": 0,
      "other": 0,
      "total": -15118
    },
    "ads": {
      "etsy_ads": -3000,
      "offsite_ads": -1500,
      "offsite_ads_credits": 0,
      "subscription": -999,
      "total": -5499
    },
    "sales": {
      "gross_sales_cents": 125000,
      "refund_cents": 8000,
      "cancelled_cents": 2000,
      "remitted_tax": {
        "sales_tax": -2000,
        "sales_tax_credits": 200,
        "vat_ep": -1200,
        "vat_ep_credits": 0,
        "total": -3000
      }
    },
    "gross_profit": {
      "gross_sales_cents": 125000,
      "fees_cents": -15000,
      "ads_cents": -5000,
      "remitted_tax_cents": -3000,
      "total": 102000
    },
    "net_profit": {
      "gross_profit_cents": 102000,
      "cogs_cents": 30000,
      "total": 72000
    }
  }
}

// ── Response WITH hourly=true (single day) ──
{
  "shop_id": "12345678",
  "shop_name": "My Etsy Shop",
  "currency": "GBP",
  "start": 1716508800,
  "end": 1716595200,
  "balance_cents": 125430,
  "paid_out_cents": 12000,
  "sales_count": 5,
  "gross_sales_cents": 45000,
  "sales_cents": 43500,
  "fees_cents": -5400,
  "ads_cents": -1200,
  "refund_cents": 1500,
  "cancelled_cents": 0,
  "recoup_cents": 0,
  "remitted_tax_cents": 0,
  "gross_profit_cents": 38400,
  "cogs_cents": 10000,
  "net_profit_cents": 28400,
  "hourly": [
    {
      "hour": 0,
      "hour_start": 1716508800,
      "hour_end": 1716512400,
      "entry_count": 0,
      "sales_count": 0,
      "gross_sales_cents": 0,
      "sales_cents": 0,
      "fees_cents": 0,
      "ads_cents": 0,
      "remitted_tax_cents": 0,
      "refund_cents": 0,
      "cancelled_cents": 0,
      "recoup_cents": 0,
      "gross_profit_cents": 0,
      "cogs_cents": 0,
      "net_profit_cents": 0,
      "paid_out_cents": 0
    },
    { "hour": 1, "..." : "..." },
    { "hour": 2, "..." : "..." },
    "... (hours 3-8 omitted for brevity) ...",
    {
      "hour": 9,
      "hour_start": 1716541200,
      "hour_end": 1716544800,
      "entry_count": 4,
      "sales_count": 2,
      "gross_sales_cents": 18000,
      "sales_cents": 18000,
      "fees_cents": -2160,
      "ads_cents": -600,
      "remitted_tax_cents": 0,
      "refund_cents": 0,
      "cancelled_cents": 0,
      "recoup_cents": 0,
      "gross_profit_cents": 15240,
      "cogs_cents": 4000,
      "net_profit_cents": 11240,
      "paid_out_cents": 0
    },
    "... (hours 10-23) ..."
  ]
}
```

### Errors

Failures answer with the shared error body described in the Errors section: `{"error": {"code", "status", "message", "hint", "docs"}}`. Branch on `code`, not on the message.
