Plans
Retrieve all available subscription plans with pricing, credit allowances, and concurrency limits. Use this to build pricing pages or validate plan capabilities.
/api/v1/packages/plansNo Authentication Required
price_monthly ascending.Request
Send a GET request with no parameters. The endpoint returns every active plan row, which is more than the plans shown on the pricing page: the same credit volume can ship in several SKUs that differ only in their PSD template ceiling. Match on slug. Array position and array length are not stable, and neither is the set of slugs.
Headers
No headers required. The endpoint is publicly accessible.
Code Examples
1curl -X GET "https://api.sudomock.com/api/v1/packages/plans"
Response
Success Response
1{2 "plans": [3 {4 "id": "a1b2c3d4-...",5 "name": "Free",6 "slug": "free",7 "tier": "free",8 "description": "Trial state of pay as you go. 500 credits, granted once.",9 "price_monthly": 0,10 "price_yearly": 0,11 "credits_per_month": 500,12 "max_concurrent_requests": 1,13 "max_concurrent_uploads": 1,14 "psd_limit": 5,15 "stripe_price_id": null,16 "stripe_price_id_yearly": null17 },18 {19 "id": "e5f6g7h8-...",20 "name": "Starter 5K",21 "slug": "starter-5k",22 "tier": "starter",23 "description": "For small stores and side projects",24 "price_monthly": 25,25 "price_yearly": 250,26 "credits_per_month": 5000,27 "max_concurrent_requests": 3,28 "max_concurrent_uploads": 2,29 "psd_limit": 150,30 "stripe_price_id": "<opaque-checkout-price-id>",31 "stripe_price_id_yearly": "<opaque-checkout-price-id>"32 },33 {34 "id": "i9j0k1l2-...",35 "name": "Pro 25K",36 "slug": "pro-25k",37 "tier": "pro",38 "description": "For growing businesses with high volume",39 "price_monthly": 89,40 "price_yearly": 890,41 "credits_per_month": 25000,42 "max_concurrent_requests": 10,43 "max_concurrent_uploads": 5,44 "psd_limit": 500,45 "stripe_price_id": "<opaque-checkout-price-id>",46 "stripe_price_id_yearly": "<opaque-checkout-price-id>"47 },48 {49 "id": "m3n4o5p6-...",50 "name": "Scale 50K",51 "slug": "scale-50k",52 "tier": "scale",53 "description": "High-volume production and platform workflows",54 "price_monthly": 199,55 "price_yearly": 1990,56 "credits_per_month": 50000,57 "max_concurrent_requests": 25,58 "max_concurrent_uploads": 10,59 "psd_limit": null,60 "stripe_price_id": "<opaque-checkout-price-id>",61 "stripe_price_id_yearly": "<opaque-checkout-price-id>"62 }63 ]64}
This example is an excerpt
psd_limit. Read the fields you need off the row whose slug you matched, and ignore rows you do not recognize rather than failing on them.The free row is a trial state, not a monthly allowance
slug: "free" reports credits_per_month: 500 because it reuses the same column as the paid rows. Those 500 credits are granted once at signup and never renew. An account sitting on this row is in trial until a card is verified and the balance is funded: renders are watermarked and output width is capped at 1,024 px. See Error Handling for the 402 responses those limits produce.Response Fields
plansarrayRequiredArray of all active subscription plans, sorted by price_monthly ascending
plans[].idstringRequiredUnique plan identifier (UUID)
plans[].namestringRequiredDisplay name of the plan (e.g., 'Starter 5K', 'Pro 25K', 'Scale 50K')
plans[].slugstringRequiredURL-friendly identifier for programmatic use (e.g., 'starter-5k', 'pro-25k')
plans[].tierenumRequiredPlan tier level: free, starter, pro, or scale
plans[].descriptionstringHuman-readable plan description (may be null)
plans[].price_monthlynumberRequiredMonthly price in USD. Paid plans start at $25/mo
plans[].price_yearlynumberRequiredYearly price in USD (discounted annual billing)
plans[].credits_per_monthintegerRequiredRender credits included per billing period. On the free row this is the one-time signup grant of 500 credits, not a recurring allowance: it is issued once and never renews.
plans[].max_concurrent_requestsintegerRequiredMaximum number of parallel render requests allowed. 1 on free, 3 on starter, 10 on pro, 25 on scale.
plans[].max_concurrent_uploadsintegerRequiredMaximum number of parallel PSD uploads allowed. 1 on free, 2 on starter, 5 on pro, 10 on scale. This is a separate budget from max_concurrent_requests and is always the smaller of the two.
plans[].psd_limitintegerHow many PSD templates the account may store on this plan. Null means unlimited. Uploading past the ceiling returns psd_limit_reached; templates already stored keep rendering.
plans[].stripe_price_idstringOpaque monthly checkout price identifier. Null on the free row, which has nothing to check out.
plans[].stripe_price_id_yearlystringOpaque annual checkout price identifier for the same plan. Null on the free row, and null on any plan not sold annually.
Plan Tiers
The tier field takes one of four values. Three of them are subscriptions. The fourth, free, is the trial state an account starts in, not something anyone buys:
| Tier | Starting Price | Best For |
|---|---|---|
free | $0 in trial, then $0.10/render | Pay as you go. In trial: 500 credits granted once, watermarked output, 1,024 px cap, 5 stored templates. Once funded: no watermark, no width cap, 500 stored templates, 25 parallel renders |
starter | $25/mo | Small stores, side projects, testing integrations |
pro | $89/mo | Growing businesses with higher volume needs |
scale | $199/mo | High-volume production, enterprise workflows |
The free row is the catalogue entry, not a funded account's limits
GET /packages/plans returns the plan catalogue exactly as stored, so the free row always reports psd_limit: 5 and max_concurrent_requests: 1. A pay-as-you-go account that has verified a card and funded its balance sits on that same row but is enforced at 500 templates and 25 parallel renders. Do not render this row as a funded customer's ceiling.Nor as a permanent one. The enforced ceilings follow the funding: they hold while the balance is positive and for 90 days after the last top-up, then the account is enforced at the trial numbers again. Neither state is visible in this endpoint, so a dashboard that caches the funded ceilings will keep showing them after they lapse.
Annual Billing Saves ~17%
price_yearly field to display annual pricing to your users. Annual billing provides significant savings compared to monthly.Common Use Cases
Building a Pricing Page
Fetch plans dynamically to keep your pricing page always in sync:
1const response = await fetch("https://api.sudomock.com/api/v1/packages/plans");2const { plans } = await response.json();34// Filter to paid plans for your pricing page5const paidPlans = plans.filter(p => p.tier !== "free");67paidPlans.forEach(plan => {8 console.log(plan.name);9 console.log(` Monthly: $${plan.price_monthly}`);10 console.log(` Yearly: $${plan.price_yearly} ($${(plan.price_yearly / 12).toFixed(2)}/mo)`);11 console.log(` Credits: ${plan.credits_per_month.toLocaleString('en-US')}/mo`);12 console.log(` Parallel renders: ${plan.max_concurrent_requests}`);13 console.log(` PSD templates: ${plan.psd_limit ?? "unlimited"}`);14});1516// Looking up one specific plan: match the slug, never the index.17const pro25k = plans.find(p => p.slug === "pro-25k");
Error Responses
1{2 "detail": "Failed to fetch subscription plans"3}