API Documentation
Use the Sagging Royalties API to programmatically interact with your data. This is useful for automating the upload of sales reports from your own scripts or systems.
Overview
- Base URL:
https://saggingroyals.com/api - Authentication: All requests must include your API Key in the
X-API-Keyheader. - Full Reference: Interactive API Documentation
Authentication
Obtain your API Key from your user profile page (https://saggingroyals.com/profile/). Copy it immediately into your password manager or other secure location, as it will only be shown once!
Header:
X-API-Key: <YOUR_API_KEY>
Sales Channels
List Channel Types
Retrieve a list of global sales channel types supported by the system (e.g., INGRAM, KDP).
GET /channel-types
Request
Response
[
{
"name": "Ingram",
"slug": "ingram",
"code": "INGRAM",
"description": "Ingram Content Group"
},
{
"name": "KDP",
"slug": "kdp",
"code": "KDP",
"description": "Kindle Direct Publishing"
}
]
List Channels
Retrieve a list of configured sales channels for the publisher.
GET /publishers/{publisher_slug}/channels
Request
Response
[
{
"name": "Ingram",
"slug": "ingram",
"type_code": "INGRAM",
"type_name": "Ingram",
"update_cadence": "MONTHLY",
"is_visible": true
}
]
Create Sales Channel
Configure a new sales channel.
POST /publishers/{publisher_slug}/channels
Request
JSON Body: {"type_code": "INGRAM", "name": "Ingram", "slug": "ingram", "update_cadence": "monthly"}
fetch("https://saggingroyals.com/api/publishers/your-slug/channels", {
method: "POST",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
type_code: "INGRAM",
name: "Ingram",
slug: "ingram"
})
})
.then(response => response.json())
.then(data => console.log(data));
Response
{
"name": "Ingram",
"slug": "ingram",
"type_code": "INGRAM",
"type_name": "Ingram",
"update_cadence": "MONTHLY",
"is_visible": true
}
Catalog & Projects
List Projects
List all projects for a publisher.
GET /publishers/{publisher_slug}/projects
Request
Response
[
{
"title": "My Book",
"slug": "my-book",
"publication_date": "2025-01-01",
"authors": ["John Doe"],
"publications": []
}
]
Create Project
Create a new project.
POST /publishers/{publisher_slug}/projects
Request
JSON Body: {"title": "My Book", "slug": "my-book", "author": "John Doe", "publication_date": "2025-01-01"}
fetch("https://saggingroyals.com/api/publishers/your-slug/projects", {
method: "POST",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
title: "My Book",
slug: "my-book",
author: "John Doe"
})
})
.then(response => response.json())
.then(data => console.log(data));
Response
{
"title": "My Book",
"slug": "my-book",
"publication_date": "2025-01-01",
"authors": ["John Doe"],
"publications": []
}
Get Project Details
Retrieve metadata for a specific project.
GET /publishers/{publisher_slug}/projects/{project_slug}
Request
Response
{
"title": "The Great Gatsby",
"slug": "the-great-gatsby",
"publication_date": "1925-04-10",
"authors": ["F. Scott Fitzgerald"],
"publications": [
{
"isbn": "978-0743273565",
"format": "Paperback",
"us_list_price": 17.00
}
]
}
Create Publication (ISBN)
Add a publication (ISBN) to a project.
POST /publishers/{publisher_slug}/projects/{project_slug}/publications
Request
JSON Body: {"isbn": "978-1234567890", "format": "Paperback", "price": 19.99, "sku": "SKU123"}
import requests
url = "https://saggingroyals.com/api/publishers/your-slug/projects/my-book/publications"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {
"isbn": "978-1234567890",
"format": "Paperback",
"price": 19.99
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/projects/my-book/publications", {
method: "POST",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
isbn: "978-1234567890",
format: "Paperback",
price: 19.99
})
})
.then(response => response.json())
.then(data => console.log(data));
Response
Export Catalog
Download the catalog as a CSV file.
GET /publishers/{publisher_slug}/catalog/export
Request
Response The response is a downloadable CSV file.
Sales Data
Upload Sales Report
To upload a CSV or Excel sales report for processing:
POST /publishers/{publisher_slug}/upload-sales-report
Request
import requests
url = "https://saggingroyals.com/api/publishers/your-slug/upload-sales-report"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
files = {"file": open("2025-10-IsdSales.csv", "rb")}
data = {"channel": "Ingram"}
response = requests.post(url, headers=headers, data=data, files=files)
print(response.json())
// Browser example (Form Data)
const formData = new FormData();
formData.append("channel", "Ingram");
formData.append("file", fileInput.files[0]);
fetch("https://saggingroyals.com/api/publishers/your-slug/upload-sales-report", {
method: "POST",
headers: { "X-API-Key": "<YOUR_API_KEY>" },
body: formData
})
.then(response => response.json())
.then(data => console.log(data));
Response
{
"status": "success",
"file_id": 1234,
"filename": "2025-10-IsdSales.csv",
"message": "File uploaded successfully"
}
Get Sales Ledger
Retrieve raw sales records.
GET /publishers/{publisher_slug}/sales/ledger
Parameters
start_date(query, date)end_date(query, date)channel(query, string): Channel slug or code
Request
Response
[
{
"id": 1,
"publication_isbn": "978-1234567890",
"publication_title": "My Book",
"channel": "Ingram",
"period_end": "2025-01-31",
"quantity": 10,
"net_receipts_usd": 150.00
}
]
Royalties
List Royalty Holders
GET /publishers/{publisher_slug}/royalty-holders
Request
Response
[
{
"id": 1,
"name": "Jane Author",
"email": "jane@example.com",
"address_line1": "123 Main St",
"city": "New York",
"state_province": "NY",
"postal_code": "10001",
"country": "USA"
}
]
Create Royalty Holder
POST /publishers/{publisher_slug}/royalty-holders
Request
JSON Body: {"name": "Jane Author", "email": "jane@example.com"}
fetch("https://saggingroyals.com/api/publishers/your-slug/royalty-holders", {
method: "POST",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
name: "Jane Author",
email: "jane@example.com"
})
})
.then(response => response.json())
.then(data => console.log(data));
Response
{
"id": 2,
"name": "Jane Author",
"email": "jane@example.com",
"address_line1": null,
"city": null,
"state_province": null,
"postal_code": null,
"country": null
}
List Royalty Payments
GET /publishers/{publisher_slug}/royalty-payments
Request
Response
[
{
"id": 101,
"holder_id": 1,
"holder_name": "Jane Author",
"amount": 500.00,
"currency": "USD",
"payment_date": "2025-01-15",
"notes": "Check #123"
}
]
Record Royalty Payment
Record an outgoing payment to a royalty holder.
POST /publishers/{publisher_slug}/royalty-payments
Request
JSON Body: {"holder_id": 1, "amount": 500.00, "date": "2025-01-15", "notes": "Check #123"}
fetch("https://saggingroyals.com/api/publishers/your-slug/royalty-payments", {
method: "POST",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
holder_id: 1,
amount: 500.00,
date: "2025-01-15"
})
})
.then(response => response.json())
.then(data => console.log(data));
Response
{
"id": 102,
"holder_id": 1,
"holder_name": "Jane Author",
"amount": 500.00,
"currency": "USD",
"payment_date": "2025-01-15",
"notes": null
}
List Royalty Statements
Retrieve a list of generated royalty statements.
GET /publishers/{publisher_slug}/statements
Request
Response
[
{
"id": 55,
"holder_name": "Jane Author",
"period_start": "2024-07-01",
"period_end": "2024-12-31",
"opening_balance": 0.00,
"earnings": 1250.00,
"payments": 500.00,
"closing_balance": 750.00,
"is_locked": true
}
]
Generate Statements
Trigger the generation of royalty statements for a period.
POST /publishers/{publisher_slug}/statements/generate
Request
JSON Body: {"period_start": "2024-07-01", "period_end": "2024-12-31"}
fetch("https://saggingroyals.com/api/publishers/your-slug/statements/generate", {
method: "POST",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
period_start: "2024-07-01",
period_end: "2024-12-31"
})
})
.then(response => response.json())
.then(data => console.log(data));
Response
{
"status": "success",
"message": "Statement generation queued for 12 holders",
"generated_count": 12
}
Download Statement PDF
Download the PDF file for a specific statement.
GET /publishers/{publisher_slug}/statements/{statement_id}/download
Request
Response The response is a downloadable PDF file.
Incoming Payments
Track payments received from sales channels (distributor payouts).
List Payment Ledger
GET /publishers/{publisher_slug}/payments/ledger
Parameters
channel(query, string): Filter by channel slugdate_start(query, date): Filter by start datedate_end(query, date): Filter by end date
Request
Response
[
{
"id": 1,
"channel": "ingram",
"period_end": "2025-03-31",
"payout_amount": 1234.56,
"payout_currency": "USD"
}
]
Upload Payment Report
Upload a payment report file from a sales channel.
POST /publishers/{publisher_slug}/payments/reports
Request
import requests
url = "https://saggingroyals.com/api/publishers/your-slug/payments/reports"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
files = {"file": open("ingram-payment-2025-03.csv", "rb")}
data = {"channel": "ingram"}
response = requests.post(url, headers=headers, data=data, files=files)
print(response.json())
const formData = new FormData();
formData.append("channel", "ingram");
formData.append("file", fileInput.files[0]);
fetch("https://saggingroyals.com/api/publishers/your-slug/payments/reports", {
method: "POST",
headers: { "X-API-Key": "<YOUR_API_KEY>" },
body: formData
})
.then(response => response.json())
.then(data => console.log(data));
Record Payment Manually
Manually record a payment received from a channel.
POST /publishers/{publisher_slug}/payments/records
Request
JSON Body: {"channel": "ingram", "payout_amount": 1234.56, "payout_currency": "USD", "period_end": "2025-03-31"}
import requests
url = "https://saggingroyals.com/api/publishers/your-slug/payments/records"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {
"channel": "ingram",
"payout_amount": 1234.56,
"payout_currency": "USD",
"period_end": "2025-03-31"
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/payments/records", {
method: "POST",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
channel: "ingram",
payout_amount: 1234.56,
payout_currency: "USD",
period_end: "2025-03-31"
})
})
.then(response => response.json())
.then(data => console.log(data));
Expenses
Track project costs that reduce the royalty pool for net-receipts projects. See Expense Tracking for a detailed explanation.
List Expenses
GET /publishers/{publisher_slug}/expenses
Parameters
project_slug(query, string): Filter by projectcategory(query, string): Filter by category (production,editorial,marketing,distribution_fulfillment,other)
Request
Response
[
{
"id": 1,
"project_slug": "my-book",
"project_title": "My Book",
"description": "Cover design",
"category": "production",
"amount": 1500.00,
"quantity": null,
"unit_cost": null,
"effective_date": "2025-03-15",
"sales_channel_name": null,
"notes": ""
}
]
Create Expense
POST /publishers/{publisher_slug}/expenses
Request JSON Body:
{
"project_slug": "my-book",
"description": "Cover design",
"category": "production",
"amount": 1500.00,
"effective_date": "2025-03-15",
"quantity": null,
"sales_channel_id": null,
"notes": "Paid to designer Jane Smith"
}
import requests
url = "https://saggingroyals.com/api/publishers/your-slug/expenses"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {
"project_slug": "my-book",
"description": "Cover design",
"category": "production",
"amount": 1500.00,
"effective_date": "2025-03-15"
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/expenses", {
method: "POST",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
project_slug: "my-book",
description: "Cover design",
category: "production",
amount: 1500.00,
effective_date: "2025-03-15"
})
})
.then(response => response.json())
.then(data => console.log(data));
Response
{
"id": 1,
"project_slug": "my-book",
"project_title": "My Book",
"description": "Cover design",
"category": "production",
"amount": 1500.00,
"quantity": null,
"unit_cost": null,
"effective_date": "2025-03-15",
"sales_channel_name": null,
"notes": ""
}
Get Expense
GET /publishers/{publisher_slug}/expenses/{expense_id}
Request
Response
Same format as the Create Expense response.
Update Expense
PATCH /publishers/{publisher_slug}/expenses/{expense_id}
Only include fields you want to change.
Request
JSON Body: {"amount": 1750.00, "notes": "Revised invoice"}
fetch("https://saggingroyals.com/api/publishers/your-slug/expenses/1", {
method: "PATCH",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
amount: 1750.00,
notes: "Revised invoice"
})
})
.then(response => response.json())
.then(data => console.log(data));
Response
Same format as the Create Expense response, with updated fields.
Delete Expense
DELETE /publishers/{publisher_slug}/expenses/{expense_id}
Request
Response
Users
List Publisher Users
List users with access to the publisher account.
GET /publishers/{publisher_slug}/users
Request
Response
[
{
"id": 1,
"email": "owner@example.com",
"role": "OWNER",
"first_name": "Publisher",
"last_name": "Owner"
},
{
"id": 2,
"email": "editor@example.com",
"role": "EDITOR",
"first_name": "Editor",
"last_name": "User"
}
]
Invite User
Invite a new user to the publisher account.
POST /publishers/{publisher_slug}/invitations
Request
JSON Body: {"email": "editor@example.com", "role": "editor"}
fetch("https://saggingroyals.com/api/publishers/your-slug/invitations", {
method: "POST",
headers: {
"X-API-Key": "<YOUR_API_KEY>",
"Content-Type": "application/json"
},
body: JSON.stringify({
email: "editor@example.com",
role: "editor"
})
})
.then(response => response.json())
.then(data => console.log(data));
Response