Introduction
# 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
```bash # 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`:
```bash export MATON_API_KEY="YOUR_API_KEY" ```
### Getting Your API Key
1. Sign in or create an account at [maton.ai](https://maton.ai) 2. Go to [maton.ai/settings](https://maton.ai/settings) 3. Copy your API key
## Connection Management
Manage your Gumroad OAuth connections at `https://ctrl.maton.ai`.
### List Connections
```bash 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
```bash 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
```bash 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
```bash 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:
```bash 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
```bash 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
```bash 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
```bash GET /gumroad/v2/products/{product_id} ```
#### Update Product
```bash PUT /gumroad/v2/products/{product_id} Content-Type: application/x-www-form-urlencoded
name=Updated%20Name&price=1000 ```
#### Enable/Disable Product
```bash PUT /gumroad/v2/products/{product_id}/disable Content-Type: application/x-www-form-urlencoded
disabled=true ```
#### Delete Product
```bash 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
```bash GET /gumroad/v2/products/{product_id}/offer_codes ```
#### Get Offer Code
```bash GET /gumroad/v2/products/{product_id}/offer_codes/{offer_code_id} ```
#### Create Offer Code
```bash 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
```bash 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
```bash DELETE /gumroad/v2/products/{product_id}/offer_codes/{offer_code_id} ```
### Sales Operations
#### List Sales
```bash 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
```bash GET /gumroad/v2/sales/{sale_id} ```
### Subscriber Operations
#### List Subscribers
```bash GET /gumroad/v2/products/{product_id}/subscribers ```
#### Get Subscriber
```bash 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
```bash 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
```bash PUT /gumroad/v2/licenses/enable Content-Type: application/x-www-form-urlencoded
product_id={product_id}&license_key={license_key} ```
#### Disable License
```bash PUT /gumroad/v2/licenses/disable Content-Type: application/x-www-form-urlencoded
product_id={product_id}&license_key={license_key} ```
#### Decrement License Uses
```bash 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
```bash 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
```bash DELETE /gumroad/v2/resource_subscriptions/{resource_subscription_id} ```
**Response:** ```json { "success": true, "message": "The resource_subscription was deleted successfully." } ```
### Variant Categories
#### List Variant Categories
```bash GET /gumroad/v2/products/{product_id}/variant_categories ```
#### Get Variant Category
```bash GET /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id} ```
#### Create Variant Category
```bash POST /gumroad/v2/products/{product_id}/variant_categories Content-Type: application/x-www-form-urlencoded
title=Size ```
#### Delete Variant Category
```bash DELETE /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id} ```
### Variants
#### List Variants
```bash GET /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id}/variants ```
#### Create Variant
```bash 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
```bash 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
```bash DELETE /gumroad/v2/products/{product_id}/variant_categories/{variant_category_id}/variants/{variant_id} ```
### Custom Fields
#### List Custom Fields
```bash GET /gumroad/v2/products/{product_id}/custom_fields ```
#### Create Custom Field
```bash POST /gumroad/v2/products/{product_id}/custom_fields Content-Type: application/x-www-form-urlencoded
name=Company%20Name&required=true ```
#### Update Custom Field
```bash PUT /gumroad/v2/products/{product_id}/custom_fields/{name} Content-Type: application/x-www-form-urlencoded
required=false ```
#### Delete Custom Field
```bash DELETE /gumroad/v2/products/{product_id}/custom_fields/{name} ```
## Pagination
Gumroad uses page-based pagination for endpoints that return lists:
```bash 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
```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
```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)
```python 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 `success` boolean field - Product creation is not available via API - products must be created through the Gumroad website - POST/PUT requests use `application/x-www-form-urlencoded` content 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 `jq` or other commands, environment variables like `$MATON_API_KEY` may 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
1. Check that the `MATON_API_KEY` environment variable is set:
```bash echo $MATON_API_KEY ```
2. Verify the API key is valid by listing connections:
```bash 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
1. Ensure your URL path starts with `gumroad`. For example:
- Correct: `https://gateway.maton.ai/gumroad/v2/user` - Incorrect: `https://gateway.maton.ai/v2/user`
## Resources
- [Gumroad API Overview](https://gumroad.com/api) - [Create API Application](https://help.gumroad.com/article/280-create-application-api) - [License Keys Help](https://help.gumroad.com/article/76-license-keys) - [Maton Community](https://discord.com/invite/dBfFAcefs2) - [Maton Support](mailto:[email protected])