TL;DR: Gumroad has a partial REST API but it can't create products end-to-end (no cover image, no description rich text, no pricing tier UI). CDP automation closes the gap. 6 SKUs created via CDP in 60 days, average 90 seconds per SKU from "I have a product idea" to "buyable URL".
What Gumroad's API does and doesn't do
What works via API
import requests
r = requests.put(
"https://api.gumroad.com/v2/products/{id}",
data={"access_token": TOKEN, "price": 1900, "name": "Updated"},
)
- ✓ Update existing product attributes (price, name, custom fields)
- ✓ Get product list, sales list
- ✓ Update tag list
- ✓ Update affiliate program
What doesn't work via API
- ✗ Create a NEW product (no /v2/products POST endpoint that exposes everything)
- ✗ Upload cover image (must use UI)
- ✗ Set rich-text description (markdown editor in browser only)
- ✗ Add downloadable file
- ✗ Configure thank-you-page redirects to specific URL
- ✗ Set discount codes per product
So for full SKU creation, CDP is required.
The CDP flow (90 sec end-to-end)
from playwright.sync_api import sync_playwright
SELECTORS = {
'create_product': 'a[href="/products/new"]',
'product_type': 'div:has-text("Digital product"):visible',
'product_name': 'input[name="name"]',
'price': 'input[name="price"]',
'description_editor': 'div.ProseMirror',
'cover_upload': 'input[type="file"][accept*="image"]',
'save_button': 'button:has-text("Save and continue")',
'publish_button': 'button:has-text("Publish")',
}
def create_sku(name, price_usd, description_md, cover_path, file_path):
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp("http://127.0.0.1:9222")
page = browser.contexts[0].new_page()
page.goto("https://gumroad.com/products")
page.click(SELECTORS['create_product'])
page.click(SELECTORS['product_type'])
page.fill(SELECTORS['product_name'], name)
page.fill(SELECTORS['price'], str(price_usd))
page.click(SELECTORS['description_editor'])
# Set description via clipboard
import pyperclip
pyperclip.copy(description_md)
page.keyboard.press("Control+V")
# Upload cover
page.set_input_files(SELECTORS['cover_upload'], cover_path)
# Upload file
upload_input = page.locator('input[type="file"]:not([accept*="image"])')
upload_input.set_input_files(file_path)
page.wait_for_selector('text=Uploaded', timeout=60_000)
page.click(SELECTORS['save_button'])
page.wait_for_url(re.compile(r'.*/products/[a-z0-9]+/edit'))
page.click(SELECTORS['publish_button'])
return page.url
Real numbers from 6 SKUs
| SKU | Price | Build time | LIVE URL slug |
|---|---|---|---|
| ASC API Toolkit | $499 | 90 sec | vszsui |
| AutoApp Dashboard | $39 | 80 sec | hmmzt |
| Indie Hacker Bundle | $25 | 95 sec | emvfkj |
| iOS Indie Launch Playbook | $19 | 100 sec | gncbck |
| 30-Day B2B Cold Email | $15 | 85 sec | jdmmy |
| 14 iOS Rejection Reasons | Free | 70 sec | sphytu |
Total catalog value: $597 in ~9 minutes of CDP runtime.
What I learned from 6 SKUs
1. Cover image is the highest-leverage field
Spend 2 min designing a great cover. It dominates conversion. CDP sets it programmatically, so I generate covers in batch via Canva API + import.
2. Tags need to be set AFTER product creation
The Gumroad new-product flow doesn't expose tags. After creation, navigate to /products/{id}/edit and set tags via API:
requests.put(f"https://api.gumroad.com/v2/products/{id}",
data={"access_token": TOKEN, "tags": "ios,indie,toolkit"})
3. File upload is async — wait for "Uploaded" text
The wait condition page.wait_for_selector('text=Uploaded', timeout=60_000) is non-negotiable. Without it, the save button submits before the file binds.
4. ProseMirror description doesn't take markdown directly
Same as Substack TipTap — paste markdown gets shown as plain text. Either:
- Convert to HTML first
- Or paste markdown then manually format (defeats automation)
5. After publish, set affiliate program via API
requests.put(f"https://api.gumroad.com/v2/products/{id}",
data={"access_token": TOKEN,
"affiliate_basis_points": 3000, # 30% commission
"shown_on_profile": True})
Source
Full Gumroad CDP automation + 30% affiliate setup:
AutoApp Dashboard ($39) includes:
-
gumroad_create_sku.py(CDP creator, this article) -
gumroad_setup_affiliates.py(batch 30% setup) -
gumroad_uploader.py(with retry + cover upload) - 4 cover image templates (Canva PSD)
If you're an indie hacker and you've been hand-creating Gumroad products, automate it. 90 sec per SKU adds up across a catalog.








