Gumroad
Access the Gumroad API with managed OAuth authentication. Manage products, view sales, verify licenses, and set up webhooks for your digital storefront.
Quick Start
# Get current user info
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/gumroad/v2/user')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://gateway.maton.ai/gumroad/v2/{resource}
The gateway proxies requests to api.gumroad.com/v2 and automatically injects your OAuth token.
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Gumroad OAuth connections at https://ctrl.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=gumroad&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'gumroad'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
json
{
"connection": {
"connection_id": "e1a4444f-2bb8-4e09-9265-3afe71b74b1f",
"status": "ACTIVE",
"creation_time": "2026-02-08T06:22:48.654579Z",
"last_updated_time": "2026-02-08T06:23:07.420381Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "gumroad",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Gumroad connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/gumroad/v2/products')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', 'e1a4444f-2bb8-4e09-9265-3afe71b74b1f')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
API Reference
User Info
Get Current User
GET /gumroad/v2/user
Response:
json
{
"success": true,
"user": {
"name": "Chris",
"currency_type": "usd",
"bio": null,
"twitter_handle": null,
"id": "1690942847664",
"user_id": "QmTtTnViFSoocHAexgLuJw==",
"url": "https://chriswave1246.gumroad.com",
"profile_url": "https://public-files.gumroad.com/...",
"email": "[email protected]",
"display_name": "Chris"
}
}
Product Operations
List Products
GET /gumroad/v2/products
Response:
json
{
"success": true,
"products": [
{
"id": "ABC123",
"name": "My Product",
"price": 500,
"currency": "usd",
"short_url": "https://gumroad.com/l/abc",
"sales_count": 10,
"sales_usd_cents": 5000
}
]
}
Get Product
GET /gumroad/v2/products/{product_id}
Update Product
PUT /gumroad/v2/products/{product_id}
Content-Type: application/x-www-form-urlencoded
name=Updated%20Name&price=1000
Enable/Disable Product
PUT /gumroad/v2/products/{product_id}/disable
Content-Type: application/x-www-form-urlencoded
disabled=true
Delete Product
DELETE /gumroad/v2/products/{product_id}
Note: Creating new products via API is not supported. Products must be created through the Gumroad website.
Offer Code Operations
List Offer Codes
GET /gumroad/v2/products/{product_id}/offer_codes
Get Offer Code
GET /gumroad/v2/products/{product_id}/offer_codes/{offer_code_id}
Create Offer Code
POST /gumroad/v2/products/{product_id}/offer_codes
Content-Type: application/x-www-form-urlencoded
name=SUMMER20&amount_off=20
Parameters:
- name - The code customers enter (required)
- amount_off - Cents or percentage off (required)
- offer_type - "cents" or "percent" (default: "cents")
- max_purchase_count - Maximum uses (optional)
Update Offer Code
PUT /gumroad/v2/products/{product_id}/offer_codes/{offer_code_id}
Content-Type: application/x-www-form-urlencoded
max_purchase_count=100
Delete Offer Code
DELETE /gumroad/v2/products/{product_id}/offer_codes/{offer_code_id}
Sales Operations
List Sales
GET /gumroad/v2/sales
Query parameters:
- after - Only sales after this date (YYYY-MM-DD)
- before - Only sales before this date (YYYY-MM-DD)
- page - Page number for pagination
Example with filters:
bash
GET /gumroad/v2/sales?after=2026-01-01&before=2026-12-31
Response:
json
{
"success": true,
"sales": [
{
"id": "sale_abc123",
"email": "[email protected]",
"seller_id": "seller123",
"product_id": "prod123",
"product_name": "My Product",
"price": 500,
"currency_symbol": "$",
"created_at": "2026-01-15T10:30:00Z"
}
]
}
Get Sale
GET /gumroad/v2/sales/{sale_id}
Subscriber Operations
List Subscribers
GET /gumroad/v2/products/{product_id}/subscribers
Get Subscriber
GET /gumroad/v2/subscribers/{subscriber_id}
Response:
json
{
"success": true,
"subscriber": {
"id": "sub123",
"product_id": "prod123",
"product_name": "Monthly Subscription",
"user_id": "user123",
"user_email": "[email protected]",
"status": "alive",
"created_at": "2026-01-01T00:00:00Z"
}
}
License Operations
Verify License
POST /gumroad/v2/licenses/verify
Content-Type: application/x-www-form-urlencoded
product_id={product_id}&license_key={license_key}
Parameters:
- product_id - The product ID (required)
- license_key - The license key to verify (required)
- increment_uses_count - Increment the use count (default: true)
Response (success):
json
{
"success": true,
"uses": 1,
"purchase": {
"seller_id": "seller123",
"product_id": "prod123",
"product_name": "My Product",
"permalink": "abc",
"email": "[email protected]",
"license_key": "ABC-123-DEF",
"quantity": 1,
"created_at": "2026-01-15T00:00:00Z"
}
}
Response (failure):
json
{
"success": false,
"message": "That license does not exist for the provided product."
}
Enable License
PUT /gumroad/v2/licenses/enable
Content-Type: application/x-www-form-urlencoded
product_id={product_id}&license_key={license_key}
Disable License
PUT /gumroad/v2/licenses/disable
Content-Type: application/x-www-form-urlencoded
product_id={product_id}&license_key={license_key}
Decrement License Uses
PUT /gumroad/v2/licenses/decrement_uses_count
Content-Type: application/x-www-form-urlencoded
product_id={product_id}&license_key={license_key}
Resource Subscriptions (Webhooks)
Subscribe to notifications for sales and other events.
List Resource Subscriptions
GET /gumroad/v2/resource_subscriptions?resource_name=sale
Parameters:
- resource_name - Required. One of: sale, refund, dispute, dispute_won, cancellation, subscription_updated, subscription_ended, subscription_restarted
Response:
json
{
"success": true,
"resource_subscriptions": [
{
"id": "wX43hzi-s7W4JfYFkxyeiQ==",
"resource_name": "sale",
"post_url": "https://example.com/webhook"
}
]
}
Delete Resource Subscription
DELETE /gumroad/v2/resource_subscriptions/{resource_subscription_id}
Response:
json
{
"success": true,
"message": "The resource_subscription was deleted successfully."
}
Variant Categories
List Variant Categories
GET /gumroad/v2/products/{product_id}/variant_categories
Get Variant Category
GET /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id}
Create Variant Category
POST /gumroad/v2/products/{product_id}/variant_categories
Content-Type: application/x-www-form-urlencoded
title=Size
Delete Variant Category
DELETE /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id}
Variants
List Variants
GET /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id}/variants
Create Variant
POST /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id}/variants
Content-Type: application/x-www-form-urlencoded
name=Large&price_difference=200
Update Variant
PUT /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id}/variants/{variant_id}
Content-Type: application/x-www-form-urlencoded
name=Extra%20Large
Delete Variant
DELETE /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id}/variants/{variant_id}
Custom Fields
List Custom Fields
GET /gumroad/v2/products/{product_id}/custom_fields
Create Custom Field
POST /gumroad/v2/products/{product_id}/custom_fields
Content-Type: application/x-www-form-urlencoded
name=Company%20Name&required=true
Update Custom Field
PUT /gumroad/v2/products/{product_id}/custom_fields/{name}
Content-Type: application/x-www-form-urlencoded
required=false
Delete Custom Field
DELETE /gumroad/v2/products/{product_id}/custom_fields/{name}
Pagination
Gumroad uses page-based pagination for endpoints that return lists:
GET /gumroad/v2/sales?page=1
GET /gumroad/v2/sales?page=2
Continue incrementing the page number until you receive an empty list.
Code Examples
JavaScript
const response = await fetch(
'https://gateway.maton.ai/gumroad/v2/products',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
Python
import os
import requests
response = requests.get(
'https://gateway.maton.ai/gumroad/v2/products',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
data = response.json()
Python (Verify License)
import os
import requests
response = requests.post(
'https://gateway.maton.ai/gumroad/v2/licenses/verify',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
data={
'product_id': 'your_product_id',
'license_key': 'CUSTOMER-LICENSE-KEY'
}
)
result = response.json()
if result['success']:
print(f"License valid! Uses: {result['uses']}")
else:
print(f"Invalid: {result['message']}")
Notes
- All responses include a
successboolean field - Product creation is not available via API - products must be created through the Gumroad website
- POST/PUT requests use
application/x-www-form-urlencodedcontent type (not JSON) - Prices are in cents (e.g., 500 = $5.00)
- License keys are case-insensitive
- Resource subscription webhooks send POST requests to your specified URL
- IMPORTANT: When piping curl output to
jqor other commands, environment variables like$MATON_API_KEYmay not expand correctly in some shell environments
Error Handling
| Status | Meaning |
|---|---|
| 400 | Missing Gumroad connection or bad request |
| 401 | Invalid or missing Maton API key |
| 404 | Resource not found (returned with success: false) |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Gumroad API |
Gumroad errors typically return HTTP 404 with a JSON body:
json
{
"success": false,
"message": "Error description"
}
Troubleshooting: API Key Issues
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Troubleshooting: Invalid App Name
- Ensure your URL path starts with
gumroad. For example:
- Correct:
https://gateway.maton.ai/gumroad/v2/user - Incorrect:
https://gateway.maton.ai/v2/user