Skip to main content
GET
https://api.dcycle.io
/
api
/
v1
/
purchases
List Purchases
const options = {
  method: 'GET',
  headers: {
    'x-api-key': '<x-api-key>',
    'x-organization-id': '<x-organization-id>',
    'x-user-id': '<x-user-id>'
  }
};

fetch('https://api.dcycle.io/api/v1/purchases', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));
{
  "page": 123,
  "size": 123,
  "total": 123,
  "total2": 123,
  "items": [
    {
      "id": "<string>",
      "organization_id": "<string>",
      "quantity": 123,
      "unit_id": "<string>",
      "unit": {},
      "sector": "<string>",
      "product_name": "<string>",
      "purchase_type": "<string>",
      "expense_type": "<string>",
      "supplier_id": "<string>",
      "supplier": {},
      "purchase_date": {},
      "last_purchase_timestamp": {},
      "recycled": 123,
      "country": "<string>",
      "frequency": "<string>",
      "description": "<string>",
      "status": "<string>",
      "co2e": 123,
      "ef": 123,
      "displayed_ggss": true,
      "custom_emission_factor_id": "<string>",
      "file_url": "<string>",
      "file_id": "<string>",
      "file_name": "<string>"
    }
  ]
}

List Purchases

Retrieve all purchases (Scope 3 - Category 1: Purchased Goods and Services) registered in your organization with pagination support and flexible filtering options.
Purchases represent Scope 3 emissions from goods and services your organization buys. This is typically one of the largest emission categories for most organizations.

Request

Headers

x-api-key
string
required
Your API key for authenticationExample: sk_live_1234567890abcdef
x-organization-id
string
required
Your organization UUIDExample: ff4adcc7-8172-45fe-9cf1-e90a6de53aa9
x-user-id
string
required
Your user UUIDExample: a1b2c3d4-e5f6-7890-abcd-ef1234567890

Query Parameters

page
integer
default:"1"
Page number for paginationExample: 1
size
integer
default:"50"
Number of items per page (max: 100)Example: 50
filter_by
string
Advanced filtering criteria (format: field:operator:value or field:value)Supported operators:
  • eq - Equals
  • neq - Not equals
  • in - In list
  • nin - Not in list
  • gt - Greater than
  • gte - Greater than or equal
  • lt - Less than
  • lte - Less than or equal
Example: "status:active" or "sector:eqIT Services"
sort_by
string
Sort criteria (format: field:asc or field:desc)Sortable fields:
  • purchase_date
  • quantity
  • co2e
  • sector
  • status
Example: "co2e:desc"

Response

page
integer
Current page number
size
integer
Number of items per page
total
integer
Total count of active purchases
total2
integer
Total count of purchases with errors or in review status
items
array
Array of purchase objects

Purchase Object Fields:

id
string
Unique purchase identifier (UUID v4)
organization_id
string
Organization UUID
quantity
float
Purchase quantity
unit_id
string
Unit UUID for the quantity
unit
object
Unit details object with id, name, type fields
sector
string
Economic sector code (NAICS or similar classification)Examples: "IT Services", "Manufacturing", "Transportation"
product_name
string
Product or service nameExamples: "Laptops", "Cloud Services", "Office Supplies"
purchase_type
string
Type of purchase calculation methodValues:
  • spend_based - Calculated based on monetary spend (most common)
  • supplier_specific - Uses supplier-specific emission factors
expense_type
string
Expense categoryValues: "goods", "services", "capital_goods", "other"
supplier_id
string
Supplier UUID (for spend_based purchases)
supplier
object
Supplier details object with business_name and country fields
purchase_date
datetime
Date of purchase (ISO 8601 format)
last_purchase_timestamp
datetime
Last update timestamp (ISO 8601 format)
recycled
float
Percentage of recycled content (0.0 to 1.0)
country
string
ISO 3166-1 alpha-2 country code where purchase was made
frequency
string
Purchase frequency for recurring purchasesValues: "once", "weekly", "monthly", "quarterly", "yearly"
description
string
Purchase description or notes
status
string
Purchase statusValues:
  • active - Successfully processed
  • success - Successfully processed (legacy)
  • error - Processing failed
  • in_review - Pending review
co2e
float
Total CO2 equivalent emissions in kg
ef
float
Emission factor used for calculation (kg CO2e per unit)
displayed_ggss
boolean
Whether to display GHG sector scope information
custom_emission_factor_id
string
UUID of custom emission factor (if using supplier-specific method)
file_url
string
URL of uploaded file (for bulk uploads)
file_id
string
UUID of uploaded file
file_name
string
Name of uploaded file

Example

curl -X GET "https://api.dcycle.io/api/v1/purchases?page=1&size=50&sort_by=co2e:desc" \
  -H "Authorization: Bearer ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}" \
  -H "x-user-id: ${DCYCLE_USER_ID}"

Successful Response

{
  "page": 1,
  "size": 50,
  "total": 245,
  "total2": 12,
  "items": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "organization_id": "org-uuid",
      "quantity": 25000.0,
      "unit_id": "eur-unit-uuid",
      "unit": {
        "id": "eur-unit-uuid",
        "name": "EUR",
        "type": "fiat_currency"
      },
      "sector": "IT Services",
      "product_name": "Cloud Computing Services",
      "purchase_type": "spend_based",
      "expense_type": "services",
      "supplier_id": "supplier-uuid",
      "supplier": {
        "business_name": "AWS EMEA SARL",
        "country": "ES"
      },
      "purchase_date": "2024-01-15T00:00:00Z",
      "last_purchase_timestamp": "2024-01-16T10:30:00Z",
      "recycled": null,
      "country": "ES",
      "frequency": "monthly",
      "description": "Monthly AWS cloud infrastructure costs",
      "status": "active",
      "co2e": 2450.75,
      "ef": 0.098,
      "displayed_ggss": true,
      "custom_emission_factor_id": null,
      "file_url": null,
      "file_id": null,
      "file_name": null
    },
    {
      "id": "b2c3d4e5-f6g7-8901-bcde-fg2345678901",
      "organization_id": "org-uuid",
      "quantity": 15000.0,
      "unit_id": "eur-unit-uuid",
      "unit": {
        "id": "eur-unit-uuid",
        "name": "EUR",
        "type": "fiat_currency"
      },
      "sector": "Office Supplies",
      "product_name": "Printer Paper",
      "purchase_type": "spend_based",
      "expense_type": "goods",
      "supplier_id": "supplier-uuid-2",
      "supplier": {
        "business_name": "Office Depot Spain",
        "country": "ES"
      },
      "purchase_date": "2024-01-10T00:00:00Z",
      "last_purchase_timestamp": "2024-01-10T14:22:00Z",
      "recycled": 0.3,
      "country": "ES",
      "frequency": "quarterly",
      "description": "Q1 2024 office paper supplies",
      "status": "active",
      "co2e": 1825.50,
      "ef": 0.122,
      "displayed_ggss": true,
      "custom_emission_factor_id": null,
      "file_url": null,
      "file_id": null,
      "file_name": null
    }
  ]
}

Common Errors

400 Bad Request

Cause: Invalid query parameters or filter format
{
  "detail": "Invalid filter format",
  "code": "VALIDATION_ERROR"
}
Solution: Verify that filter_by and sort_by follow the correct format.

403 Forbidden

Cause: Organization ID doesn’t match your API key or user doesn’t belong to organization
{
  "detail": "Not authorized",
  "code": "FORBIDDEN"
}
Solution: Verify that x-organization-id matches your API key’s organization.

Use Cases

Get Purchases Dashboard

Display all active purchases with highest emissions:
def get_purchases_dashboard():
    """Get purchases dashboard with top emitters"""

    response = requests.get(
        "https://api.dcycle.io/api/v1/purchases",
        headers=headers,
        params={
            "page": 1,
            "size": 100,
            "sort_by": "co2e:desc",
            "filter_by": "status:active"
        }
    )

    purchases = response.json()

    # Calculate totals
    total_emissions = sum(p['co2e'] or 0 for p in purchases['items'])
    total_spend = sum(
        p['quantity'] for p in purchases['items']
        if p['unit']['type'] == 'fiat_currency'
    )

    return {
        'purchases': purchases['items'],
        'total_active': purchases['total'],
        'total_errors': purchases['total2'],
        'total_emissions': total_emissions,
        'total_spend': total_spend
    }

# Usage
dashboard = get_purchases_dashboard()
print(f"Total emissions: {dashboard['total_emissions']:.2f} kg CO2e")
print(f"Total spend: {dashboard['total_spend']:.2f} EUR")

Filter by Sector

Get all purchases in a specific sector:
def get_purchases_by_sector(sector_name):
    """Get purchases filtered by sector"""

    response = requests.get(
        "https://api.dcycle.io/api/v1/purchases",
        headers=headers,
        params={
            "filter_by": f"sector:eq{sector_name}",
            "page": 1,
            "size": 100
        }
    )

    purchases = response.json()

    print(f"Purchases in {sector_name}: {purchases['total']}")
    for purchase in purchases['items']:
        print(f"  - {purchase['product_name']}: {purchase['co2e']:.2f} kg CO2e")

    return purchases

# Example: Get all IT Services purchases
it_purchases = get_purchases_by_sector("IT Services")

Filter by Date Range

Get purchases within a specific time period:
from datetime import datetime, timedelta

def get_recent_purchases(days=30):
    """Get purchases from the last N days"""

    # Note: Date filtering requires timestamp comparison
    # This example shows pagination through all results and client-side filtering
    all_purchases = []
    page = 1
    cutoff_date = datetime.now() - timedelta(days=days)

    while True:
        response = requests.get(
            "https://api.dcycle.io/api/v1/purchases",
            headers=headers,
            params={"page": page, "size": 100}
        )

        data = response.json()

        # Filter by date client-side
        for purchase in data['items']:
            purchase_date = datetime.fromisoformat(purchase['purchase_date'].replace('Z', '+00:00'))
            if purchase_date >= cutoff_date:
                all_purchases.append(purchase)

        if len(data['items']) < 100:
            break

        page += 1

    print(f"Purchases in last {days} days: {len(all_purchases)}")
    return all_purchases

# Get purchases from last 30 days
recent = get_recent_purchases(30)

Get Purchases with Errors

Identify and review purchases that failed processing:
def get_error_purchases():
    """Get purchases with errors for review"""

    response = requests.get(
        "https://api.dcycle.io/api/v1/purchases",
        headers=headers,
        params={
            "filter_by": "status:eqerror",
            "page": 1,
            "size": 100
        }
    )

    purchases = response.json()

    print(f"⚠️ Purchases with errors: {len(purchases['items'])}")

    for purchase in purchases['items']:
        print(f"\nPurchase ID: {purchase['id']}")
        print(f"  Product: {purchase['product_name']}")
        print(f"  Sector: {purchase['sector']}")
        print(f"  Date: {purchase['purchase_date']}")
        print(f"  Status: {purchase['status']}")

    return purchases

# Review error purchases
errors = get_error_purchases()

Analyze Emissions by Sector

Group purchases and calculate emissions by sector:
def analyze_emissions_by_sector():
    """Analyze total emissions grouped by sector"""

    # Get all active purchases
    all_purchases = []
    page = 1

    while True:
        response = requests.get(
            "https://api.dcycle.io/api/v1/purchases",
            headers=headers,
            params={
                "page": page,
                "size": 100,
                "filter_by": "status:active"
            }
        )

        data = response.json()
        all_purchases.extend(data['items'])

        if len(all_purchases) >= data['total']:
            break

        page += 1

    # Group by sector
    sectors = {}
    for purchase in all_purchases:
        sector = purchase['sector']
        if sector not in sectors:
            sectors[sector] = {
                'count': 0,
                'total_co2e': 0,
                'total_spend': 0
            }

        sectors[sector]['count'] += 1
        sectors[sector]['total_co2e'] += purchase['co2e'] or 0

        if purchase['unit']['type'] == 'fiat_currency':
            sectors[sector]['total_spend'] += purchase['quantity']

    # Sort by emissions
    sorted_sectors = sorted(
        sectors.items(),
        key=lambda x: x[1]['total_co2e'],
        reverse=True
    )

    print("Emissions by Sector:")
    print("-" * 70)
    for sector, data in sorted_sectors:
        print(f"{sector}:")
        print(f"  Purchases: {data['count']}")
        print(f"  Emissions: {data['total_co2e']:.2f} kg CO2e")
        print(f"  Spend: {data['total_spend']:.2f} EUR")
        print()

    return sorted_sectors

# Analyze emissions
analysis = analyze_emissions_by_sector()

Purchase Types

Spend-Based Purchases

Description: Emissions calculated based on monetary spend using economic input-output emission factors. Characteristics:
  • Most common method (80-90% of purchases)
  • Requires: quantity (in currency), sector, country
  • Uses standardized emission factors per EUR/USD spent
  • Less accurate but practical for most purchases
Example: €25,000 spent on IT services

Supplier-Specific Purchases

Description: Emissions calculated using supplier-provided product-specific emission factors. Characteristics:
  • More accurate than spend-based
  • Requires: product name, supplier, specific emission factor
  • Used when supplier provides LCA data
  • Ideal for major suppliers or critical products
Example: 1000 kg of supplier-certified sustainable paper

Expense Types

TypeDescriptionScope 3 Category
goodsPhysical productsCategory 1 - Purchased Goods
servicesProfessional and technical servicesCategory 1 - Purchased Services
capital_goodsLong-lived assets (machinery, buildings)Category 2 - Capital Goods
otherMiscellaneous purchasesCategory 1

Frequency Options

Recurring purchases can be tracked with frequency:
FrequencyDescription
onceOne-time purchase
weeklyWeekly recurring
monthlyMonthly recurring
quarterlyQuarterly recurring
yearlyAnnual recurring

Best Practices

1. Use Pagination Effectively

When you have many purchases, use pagination:
# Good - Paginate through results
def get_all_purchases():
    all_purchases = []
    page = 1

    while True:
        response = requests.get(
            "https://api.dcycle.io/api/v1/purchases",
            headers=headers,
            params={"page": page, "size": 100}
        )

        data = response.json()
        all_purchases.extend(data['items'])

        if len(all_purchases) >= data['total']:
            break

        page += 1

    return all_purchases

2. Filter for Performance

Use server-side filtering instead of loading everything:
# Good - Server-side filtering
response = requests.get(
    "https://api.dcycle.io/api/v1/purchases",
    headers=headers,
    params={"filter_by": "sector:eqIT Services"}
)

# Bad - Client-side filtering
response = requests.get("https://api.dcycle.io/api/v1/purchases", headers=headers)
it_purchases = [p for p in response.json()['items'] if p['sector'] == 'IT Services']

3. Monitor Error Status

Regularly check for purchases with errors:
# Check error count
response = requests.get(
    "https://api.dcycle.io/api/v1/purchases",
    headers=headers,
    params={"page": 1, "size": 1}
)

error_count = response.json()['total2']
if error_count > 0:
    print(f"⚠️ {error_count} purchases need attention")

4. Sort by Emissions

Focus on high-impact purchases first:
# Get top 20 emission sources
response = requests.get(
    "https://api.dcycle.io/api/v1/purchases",
    headers=headers,
    params={
        "sort_by": "co2e:desc",
        "page": 1,
        "size": 20
    }
)

top_emitters = response.json()['items']