> ## Documentation Index
> Fetch the complete documentation index at: https://code.dcycle.io/llms.txt
> Use this file to discover all available pages before exploring further.

# List Existing Country Sales

> The product-country combinations already recorded for a period, so a CSV upload can skip duplicates instead of creating them

[← Sold Products API](/api-reference/sold-products/overview)

List the product-and-country combinations that already have sales recorded in a date range. This exists for deduplication: before uploading a CSV of country sales, call it to find out which rows would land on top of data that is already there.

<Warning>
  **This endpoint descends into child organizations; [Check Period Overlap](/api-reference/sold-products/check-overlap) does not.** Two endpoints in the same group, opposite scopes.

  The response covers the organization in the header **and every child organization below it**, which is why results come grouped by `organization_id`. A combination reported here may belong to a subsidiary, not to the organization you queried.
</Warning>

## Request

### Headers

<ParamField header="x-organization-id" type="string" required>
  UUID of the organization to start from. Its children are included automatically.

  **Format:** UUID
</ParamField>

<ParamField header="x-api-key" type="string">
  Your API key.
</ParamField>

### Query Parameters

<ParamField query="start_date" type="string" required>
  Start of the range. Periods **overlapping** this range are returned — a period is not required to sit entirely inside it.

  **Format:** `YYYY-MM-DD`
</ParamField>

<ParamField query="end_date" type="string" required>
  End of the range.

  **Format:** `YYYY-MM-DD`
</ParamField>

## Response

The response is **nested two levels deep**: an outer `items` grouped by organization, each holding its own `items` of combinations.

<ResponseField name="items" type="array[object]">
  One entry per organization that has matching sales.

  <Expandable title="Organization Group">
    <ResponseField name="organization_id" type="string">
      UUID of the organization these combinations belong to — the one from your header, or one of its children.
    </ResponseField>

    <ResponseField name="items" type="array[object]">
      The product-country combinations recorded for this organization.

      <Expandable title="Combination">
        <ResponseField name="product_name" type="string">
          Name of the sold product.
        </ResponseField>

        <ResponseField name="country_name" type="string">
          Country the sales were recorded for.
        </ResponseField>

        <ResponseField name="period_start_date" type="date">
          First day of the period holding these sales.
        </ResponseField>

        <ResponseField name="period_end_date" type="date">
          Last day of that period.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  **Combinations are named, not identified.** Rows come back as `product_name` and `country_name` strings rather than ids, because the point is to compare them against the text in a CSV about to be uploaded. Match on the same casing and spelling your file uses.
</Note>

## Example

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X GET "https://api.dcycle.io/v1/sold-products/existing-country-sales?start_date=2026-01-01&end_date=2026-12-31" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "x-organization-id: YOUR_ORGANIZATION_ID"
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import requests

  HEADERS = {
      "x-api-key": "YOUR_API_KEY",
      "x-organization-id": "YOUR_ORGANIZATION_ID",
  }

  data = requests.get(
      "https://api.dcycle.io/v1/sold-products/existing-country-sales",
      headers=HEADERS,
      params={"start_date": "2026-01-01", "end_date": "2026-12-31"},
      timeout=30,
  ).json()

  # Flatten the two levels into a set you can test rows against
  already = {
      (item["product_name"], item["country_name"])
      for group in data["items"]
      for item in group["items"]
  }

  rows_to_upload = [r for r in csv_rows if (r["product"], r["country"]) not in already]
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const params = new URLSearchParams({
    start_date: "2026-01-01",
    end_date: "2026-12-31",
  });
  const response = await fetch(
    `https://api.dcycle.io/v1/sold-products/existing-country-sales?${params}`,
    {
      headers: {
        "x-api-key": "YOUR_API_KEY",
        "x-organization-id": "YOUR_ORGANIZATION_ID",
      },
    },
  );
  const data = await response.json();

  const already = new Set(
    data.items.flatMap((group) =>
      group.items.map((i) => `${i.product_name}|${i.country_name}`),
    ),
  );
  ```
</CodeGroup>

### Successful Response

Returns `200 OK`.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "items": [
    {
      "organization_id": "a8315ef3-dd50-43f8-b7ce-d839e68d51fa",
      "items": [
        {
          "product_name": "Recycled paper ream",
          "country_name": "Spain",
          "period_start_date": "2026-01-01",
          "period_end_date": "2026-06-30"
        }
      ]
    }
  ]
}
```

An organization with no matching sales simply does not appear in `items` — there is no empty group for it.

## Common Errors

### 422 Unprocessable Entity

**Cause:** `start_date` or `end_date` is missing. Both are required.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "detail": [
    {
      "loc": ["query", "start_date"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}
```

## Use Cases

### Skip duplicate rows instead of creating them

Before uploading country sales in bulk, fetch the combinations that already exist for the period and filter your file against them. Uploading a combination that is already recorded adds sales on top of sales, and the resulting total looks plausible — which is what makes it hard to notice later.

### Understand a holding before loading into it

Because the response descends into children, it also answers "has any subsidiary already reported this product for this country?". That is worth checking before a parent-level upload, since the duplicate would otherwise land in a different organization from the one you are working in.

## Related Endpoints

<CardGroup cols={2}>
  <Card title="Check Period Overlap" icon="calendar-xmark" href="/api-reference/sold-products/check-overlap">
    The other pre-upload check — single organization only
  </Card>

  <Card title="Country Sales" icon="earth-europe" href="/api-reference/sold-products/country-sales">
    Record the sales themselves
  </Card>

  <Card title="List Periods" icon="calendar" href="/api-reference/sold-products/list-periods">
    The periods these combinations sit in
  </Card>

  <Card title="Sold Products API" icon="box" href="/api-reference/sold-products/overview">
    Everything the Sold Products API covers
  </Card>
</CardGroup>
