To integrate Printful with Bolt.new, get your API key from the Printful Dashboard under Settings → API, then use Next.js API routes to browse the product catalog, generate mockup images, and create orders via Printful's REST API. All communication uses HTTPS — fully compatible with Bolt's WebContainer. No inventory or manufacturing needed — Printful handles printing, packaging, and shipping on demand.
Build a Print-on-Demand Storefront with Printful and Bolt.new
Printful eliminates the traditional barriers to selling custom-printed merchandise: you do not need to buy inventory, set up printing equipment, or handle shipping. You upload a design, choose products from Printful's catalog, and your customers order through your store. Printful handles the entire fulfillment chain — printing, quality control, packaging, and worldwide shipping — and charges you only after a customer places an order. Your margin is the difference between what you charge customers and what Printful charges you.
The Printful REST API exposes this entire workflow programmatically. You can fetch the full product catalog (500+ products including t-shirts, hoodies, mugs, phone cases, posters, and more) with pricing and available variants. The Mockup Generator API lets you upload a design file and get back photorealistic mockup images showing your design on the physical product — the same mockup images you display on your storefront product pages. When a customer purchases, you call the Order API to create a fulfillment order that Printful processes automatically.
Authentication is simple: a static API key sent as a Bearer token in the Authorization header. There is no OAuth flow, no redirect URI setup, and no session management. The API key is sensitive (it allows creating orders charged to your Printful account) so always keep it in server-side API routes, never in client-side code. The HTTPS-based REST API works perfectly inside Bolt's WebContainer, so you can browse the catalog, test mockup generation, and prototype your storefront UI entirely within the Bolt preview before deploying.
Integration method
Printful's REST API uses simple token-based authentication (an API key in the Authorization header) and communicates over HTTPS — architecturally compatible with Bolt's WebContainer. You browse Printful's catalog and generate mockups from API routes, then pass order data to Printful when a customer purchases. No inventory, no manufacturing, and no shipping logistics to manage in your app — Printful handles all fulfillment after you submit the order.
Prerequisites
- A Printful account at printful.com — free to create, no monthly fee
- Your Printful API key from Dashboard → Settings → API (bottom of the page)
- A Bolt.new project using Next.js for server-side API routes
- A publicly accessible URL for design/artwork images (Supabase Storage, AWS S3, or Cloudinary for hosting images that Printful's mockup generator fetches)
- Basic understanding of e-commerce concepts (products, variants, orders)
Step-by-step guide
Get Your Printful API Key
Get Your Printful API Key
Log in to your Printful account at printful.com. In the left sidebar, go to Dashboard → Settings. Scroll to the bottom of the Settings page to find the 'API' section. Click 'Enable API Access' if it is not already enabled, then click 'Create API Key'. Give your key a descriptive name (e.g., 'Bolt Dev App') and copy the generated key immediately — Printful shows it only once. Add the API key to your Bolt project's .env file as PRINTFUL_API_KEY. All Printful API requests use Bearer token authentication: include Authorization: Bearer {your_api_key} in every request header. Unlike OAuth flows, this is a static credential — it does not expire, but it grants full API access to your Printful account including creating orders (which incur charges), so keep it strictly server-side. Printful's API base URL is https://api.printful.com. The API supports both API key authentication (what you're using) and OAuth for multi-store platforms. For your own store, API key authentication is the correct approach. Printful has two API versions — v1 (legacy) and v2 (current, recommended). Use v2 endpoints where available, identifiable by the /v2 prefix in the path.
Set up the project for Printful API integration. Create a .env.local file with PRINTFUL_API_KEY=your_key_here as a placeholder. Create a src/lib/printful.ts helper that exports a printfulFetch(path, options?) function — it calls https://api.printful.com/{path} with the Authorization: Bearer ${process.env.PRINTFUL_API_KEY} header and returns the parsed JSON response. Include error handling that throws if response.code is not 200. Use TypeScript.
Paste this in Bolt.new chat
1// src/lib/printful.ts2const PRINTFUL_BASE = 'https://api.printful.com';34interface PrintfulResponse<T> {5 code: number;6 result: T;7 error?: string;8}910export async function printfulFetch<T>(11 path: string,12 options: RequestInit = {}13): Promise<T> {14 const apiKey = process.env.PRINTFUL_API_KEY;15 if (!apiKey) throw new Error('PRINTFUL_API_KEY is not set');1617 const response = await fetch(`${PRINTFUL_BASE}/${path}`, {18 ...options,19 headers: {20 Authorization: `Bearer ${apiKey}`,21 'Content-Type': 'application/json',22 ...options.headers,23 },24 });2526 const data: PrintfulResponse<T> = await response.json();2728 if (data.code !== 200) {29 throw new Error(`Printful API error ${data.code}: ${data.error ?? 'Unknown error'}`);30 }3132 return data.result;33}Pro tip: Printful API keys have no expiry but should be rotated if compromised. If you accidentally commit your API key to GitHub, immediately go to Printful Dashboard → Settings → API, delete the exposed key, and create a new one.
Expected result: A reusable printfulFetch helper is available in src/lib/printful.ts. Your .env file has the PRINTFUL_API_KEY placeholder. All API routes can import this helper to make authenticated Printful requests.
Browse the Printful Product Catalog
Browse the Printful Product Catalog
Printful's catalog API at /products lists all available blank products — the items Printful can print on. Each product has an ID, name, brand, model, and an array of variants (specific size/color combinations). Variants have individual pricing, dimensions, and availability status. For building a storefront, you typically want to work with Printful 'sync products' (products you've set up in your Printful store with your design applied and prices set) rather than the raw catalog. Sync products are accessed via /store/products. However, if you are building a dynamic product designer where users upload their own designs, you'll work with the raw catalog to get product IDs and variant IDs. The catalog endpoint returns up to 100 products per request. Use the limit and offset parameters for pagination. For a storefront, you might filter to specific product categories (e.g., only apparel) by checking the type_name field. Cache catalog responses in your application — the catalog changes infrequently and caching reduces API calls. All catalog data is public information (Printful prices and product specs are visible on their website), so you could theoretically fetch it client-side. However, all API calls should go through server-side API routes to keep your API key secure — the same key that accesses the catalog also accesses order creation.
Create /api/printful/products/route.ts that calls the Printful catalog API at products?limit=20&category_id=1 (category 1 = men's t-shirts) and returns a cleaned list with productId, name, image, description, and starting price (minimum variant price). Also create /api/printful/products/[id]/route.ts that fetches a single product with all variants, returning variant details including size, color, price, and in_stock status. Use the printfulFetch helper from src/lib/printful.ts.
Paste this in Bolt.new chat
1// app/api/printful/products/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { printfulFetch } from '@/lib/printful';45interface PrintfulVariant {6 id: number;7 price: string;8 in_stock: boolean;9}1011interface PrintfulProduct {12 id: number;13 title: string;14 image: string;15 description: string;16 variants: PrintfulVariant[];17}1819export async function GET(request: NextRequest) {20 try {21 const limit = request.nextUrl.searchParams.get('limit') ?? '20';22 const categoryId = request.nextUrl.searchParams.get('category_id') ?? '';2324 const queryParts = [`limit=${limit}`, 'offset=0'];25 if (categoryId) queryParts.push(`category_id=${categoryId}`);2627 const products = await printfulFetch<PrintfulProduct[]>(28 `products?${queryParts.join('&')}`29 );3031 const cleaned = products.map(p => ({32 id: p.id,33 title: p.title,34 image: p.image,35 description: p.description,36 startingPrice: p.variants.length37 ? Math.min(...p.variants.map(v => parseFloat(v.price))).toFixed(2)38 : null,39 variantCount: p.variants.length,40 }));4142 return NextResponse.json({ products: cleaned });43 } catch (err: any) {44 return NextResponse.json({ error: err.message }, { status: 500 });45 }46}Pro tip: Printful's catalog has 500+ products across many categories. Filter by category_id to narrow results for your storefront. Category IDs: 1 = Men's T-Shirts, 2 = Women's T-Shirts, 6 = Posters, 12 = Mugs, 14 = Phone Cases. Find all category IDs at https://api.printful.com/categories.
Expected result: The /api/printful/products endpoint returns a list of Printful products with names, images, and starting prices. The /api/printful/products/[id] endpoint returns full variant details. Your Bolt preview can display a working product grid populated from real Printful catalog data.
Generate Product Mockups
Generate Product Mockups
Printful's Mockup Generator API creates photorealistic product preview images showing your design on the physical product. This is how you generate the product images displayed on your storefront — instead of relying on generic product photos, you show exactly what the customer's item will look like with your design applied. The mockup generation workflow has two steps. First, call POST /mockup-generator/create-task/{product_id} with the variant IDs and the design file URL. Printful returns a task key. Second, poll GET /mockup-generator/task?task_key={key} until the task status is 'completed', then extract the generated image URLs from the result. Your design image must be publicly accessible via HTTPS — Printful fetches it from the URL you provide. Printful supports PNG (recommended), JPG, and SVG. For transparent backgrounds on apparel, always use PNG. The image should be high resolution (at least 150 DPI for the print area size) — Printful specifies minimum dimensions for each product in the print file guide. Mockup generation typically takes 5-30 seconds. For a responsive UI, trigger the task, show a loading spinner, poll the task endpoint every 3 seconds, and display the mockup when ready. For a production storefront, generate and cache mockup images at design-time rather than generating them on every product page load. Note: mockup generation during development in the Bolt preview requires your design image to be hosted at a publicly accessible URL. Images only accessible from within the WebContainer (in-memory files) cannot be fetched by Printful's servers.
Create /api/printful/mockup/route.ts that accepts { productId, variantIds, imageUrl } in the POST body. First call POST https://api.printful.com/mockup-generator/create-task/{productId} with the variantIds array and imageUrl as the file_url. Then poll GET /mockup-generator/task?task_key={key} every 2 seconds (up to 30 seconds) until status is 'completed'. Return the mockup image URLs from the completed task. Handle 'failed' status with an error response. Use async/await and the printfulFetch helper.
Paste this in Bolt.new chat
1// app/api/printful/mockup/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { printfulFetch } from '@/lib/printful';45interface MockupTask {6 task_key: string;7 status: string;8 mockups?: Array<{9 placement: string;10 mockup_url: string;11 extra: Array<{ title: string; url: string }>;12 }>;13}1415export async function POST(request: NextRequest) {16 const { productId, variantIds, imageUrl } = await request.json();1718 try {19 // Step 1: Create mockup generation task20 const task = await printfulFetch<MockupTask>(21 `mockup-generator/create-task/${productId}`,22 {23 method: 'POST',24 body: JSON.stringify({25 variant_ids: variantIds,26 files: [{ placement: 'front', image_url: imageUrl, position: { area_width: 1800, area_height: 2400, width: 1800, height: 1800, top: 300, left: 0 } }],27 format: 'jpg',28 width: 1000,29 }),30 }31 );3233 const taskKey = task.task_key;3435 // Step 2: Poll for result (up to 60 seconds)36 for (let attempt = 0; attempt < 20; attempt++) {37 await new Promise(resolve => setTimeout(resolve, 3000)); // wait 3s3839 const result = await printfulFetch<MockupTask>(40 `mockup-generator/task?task_key=${taskKey}`41 );4243 if (result.status === 'completed') {44 const mockupUrls = result.mockups?.map(m => ({45 placement: m.placement,46 url: m.mockup_url,47 })) ?? [];48 return NextResponse.json({ mockups: mockupUrls });49 }5051 if (result.status === 'failed') {52 return NextResponse.json({ error: 'Mockup generation failed' }, { status: 500 });53 }54 }5556 return NextResponse.json({ error: 'Mockup generation timed out' }, { status: 408 });57 } catch (err: any) {58 return NextResponse.json({ error: err.message }, { status: 500 });59 }60}Pro tip: Printful mockup generation can take 10-30 seconds. For production, generate mockups once when you add a product to your store and save the resulting image URLs in your database. Regenerating on every product page load is slow and unnecessary unless designs change frequently.
Expected result: The /api/printful/mockup endpoint creates a mockup generation task, polls until completion, and returns the mockup image URLs. Your storefront displays photorealistic product previews showing your design on Printful products.
Create Orders via the Printful API
Create Orders via the Printful API
When a customer completes a purchase in your storefront, call the Printful Order API to create a fulfillment order. Printful receives the order, prints the product, and ships it directly to your customer. You are charged Printful's fulfillment cost; you've already collected payment from the customer through your payment processor (Stripe, etc.). A Printful order requires: recipient (name, address, city, country, zip), and items (each with a sync_variant_id for Printful sync products, or a variant_id plus file_url for dynamic print items). For orders placed in test mode (confirm: false), Printful records the order but does not process or charge it — perfect for development. The recommended production flow: collect payment via Stripe, verify payment success via webhook, then call the Printful order API. Never create Printful orders before payment is confirmed — you'll incur fulfillment costs for orders that weren't paid. The Printful order API is synchronous and returns the order ID immediately upon acceptance. Note that Printful order creation requires your deployed server for production webhooks — incoming Stripe payment confirmation webhooks cannot be received by Bolt's WebContainer. For development testing, you can manually trigger order creation from your Bolt preview using test variant IDs and confirm: false to create draft orders without incurring charges.
Create /api/printful/orders/route.ts that accepts order data including recipient (name, address fields) and items (variantId, quantity, fileUrl, price) in the POST body. Call POST https://api.printful.com/orders with confirm: false for development (draft mode) or confirm: true for production. Return the Printful order ID and order status. Add an order confirmation page at /order-confirmation/[orderId] that fetches the order details from GET /api/printful/orders/[id] and displays shipping address, items ordered, and estimated fulfillment time.
Paste this in Bolt.new chat
1// app/api/printful/orders/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { printfulFetch } from '@/lib/printful';45interface PrintfulOrder {6 id: number;7 external_id: string;8 status: string;9 shipping: string;10 created: number;11}1213export async function POST(request: NextRequest) {14 const { recipient, items, externalId } = await request.json();1516 // Validate required fields17 if (!recipient?.address1 || !recipient?.city || !recipient?.country_code) {18 return NextResponse.json({ error: 'Complete shipping address required' }, { status: 400 });19 }2021 try {22 const order = await printfulFetch<PrintfulOrder>('orders', {23 method: 'POST',24 body: JSON.stringify({25 external_id: externalId ?? `order_${Date.now()}`,26 shipping: 'STANDARD', // Standard shipping27 recipient: {28 name: recipient.name,29 address1: recipient.address1,30 address2: recipient.address2 ?? '',31 city: recipient.city,32 state_code: recipient.state_code ?? '',33 country_code: recipient.country_code,34 zip: recipient.zip,35 email: recipient.email,36 },37 items: items.map((item: any) => ({38 sync_variant_id: item.syncVariantId,39 quantity: item.quantity ?? 1,40 retail_price: item.retailPrice, // The price you charged the customer41 })),42 // Use confirm: false during development — creates a draft order without processing43 confirm: process.env.NODE_ENV === 'production',44 }),45 });4647 return NextResponse.json({48 orderId: order.id,49 externalId: order.external_id,50 status: order.status,51 }, { status: 201 });52 } catch (err: any) {53 return NextResponse.json({ error: err.message }, { status: 500 });54 }55}Pro tip: During development, always pass confirm: false to create Printful draft orders without incurring charges. Check the Printful dashboard to verify draft orders appear correctly before switching to confirm: true in production.
Expected result: The /api/printful/orders endpoint creates a Printful fulfillment order. In development with confirm: false, orders appear in the Printful dashboard as drafts without being processed. In production, orders trigger automatic fulfillment and shipping.
Common use cases
Custom Merchandise Storefront
Build a branded merchandise store where customers browse products, see mockups with your design applied, select sizes and colors, and check out. Printful handles all fulfillment. This is the complete e-commerce flow with zero inventory management.
Build a merchandise store using the Printful API. Create API routes to: fetch the Printful catalog at /api/printful/products, get variants for a specific product at /api/printful/products/[id]/variants, and list my existing sync products at /api/printful/store/products. Build a product grid page showing product thumbnails, names, and starting prices. Each product links to a detail page with size/color selector, mockup images, and an Add to Cart button. The cart uses React state. Store PRINTFUL_API_KEY in .env. Use TypeScript.
Copy this prompt to try it in Bolt.new
Custom Product Designer
Create an interactive product customizer where users upload their own image, choose a product type, and preview their design on a photorealistic mockup in real time. The mockup generation API call happens server-side but displays results instantly in the browser.
Build a product customizer where users can upload a PNG logo, select a product from a dropdown (fetch Printful catalog via API), and see a mockup of their design on the product. Create a /api/printful/mockup route that accepts productId, variantId, and imageUrl (a publicly accessible URL), then calls the Printful Mockup Generator API to queue a mockup task and poll for the result. Display the generated mockup image when ready. Add a 'Order Now' button that creates a Printful order via /api/printful/orders.
Copy this prompt to try it in Bolt.new
Artist Portfolio with Merch Sales
Artists and creators can sell prints of their digital artwork on Printful's poster and canvas products. Build a gallery page where each artwork has a 'Buy Print' button that creates a Printful order for a museum-quality poster with the artwork image, available in multiple sizes.
Create an artist portfolio with integrated print sales. For each artwork image in the gallery, add a 'Buy Print' button that opens a modal showing the artwork on a Printful poster mockup (use the Mockup Generator API). Include size options (fetched from Printful variant API). When the user clicks Purchase, call /api/printful/orders to create a Printful order with the artwork image file URL, selected variant, and the customer's shipping address from a form. Show the Printful order ID as a confirmation.
Copy this prompt to try it in Bolt.new
Troubleshooting
Printful API returns 401 Unauthorized for every request
Cause: The PRINTFUL_API_KEY environment variable is missing, incorrectly formatted, or the API key has been deleted from the Printful dashboard. Printful also returns 401 if you use the API key in the wrong format.
Solution: Verify the API key exists in your .env file as PRINTFUL_API_KEY. Confirm the Authorization header is formatted as 'Bearer YOUR_KEY' (with the word Bearer and a space). Check the Printful dashboard under Settings → API to confirm the key is still active — keys can be deleted manually or invalidated if Printful detects suspicious activity.
1// Correct Authorization header format2headers: {3 'Authorization': `Bearer ${process.env.PRINTFUL_API_KEY}`,4 'Content-Type': 'application/json',5}Mockup generation returns task status 'failed' without a clear error message
Cause: Common causes: the design image URL is not publicly accessible (a localhost or WebContainer URL), the image resolution is too low for the product's print area, or the file URL returns a non-200 status code when Printful fetches it.
Solution: Ensure the imageUrl you pass to the mockup API is a publicly accessible HTTPS URL — Printful's servers must be able to fetch it. During development, host your test design images on Imgur, Cloudinary free tier, or Supabase Storage with a public URL. Check that the image meets Printful's minimum size requirements for the selected product.
Printful order creation succeeds but customer receives wrong product or size
Cause: The sync_variant_id passed in the order items does not match the product variant the customer selected. Printful sync variant IDs are specific to your store's configured products, not the generic catalog variant IDs.
Solution: Use GET /store/products to list your store's sync products, and GET /store/products/{id} to get the sync variant IDs for each size/color. These sync variant IDs (not the generic catalog variant IDs) are what you pass in order items. Keep a mapping of your displayed variants to their sync_variant_ids in your database.
Best practices
- Always use confirm: false when creating Printful orders during development — this creates a draft without triggering fulfillment or incurring charges, visible in your Printful dashboard for verification.
- Generate and cache mockup images at design-time rather than on every product page load — mockup generation takes 10-30 seconds and should not block your product pages from loading.
- Host design images for mockup generation on a CDN or public storage bucket (Supabase Storage, S3, Cloudinary) — Printful's servers must be able to fetch the image via public HTTPS URL.
- Use Printful's retail_price field in order items to track the price you charged the customer — this appears on shipping labels and return documentation.
- Never create Printful orders before confirming payment from your payment processor — use Stripe webhooks on your deployed server to trigger order creation only after payment_intent.succeeded.
- Cache the Printful product catalog in your database or memory — it changes infrequently and caching reduces API calls and speeds up product page loads.
- Check variant in_stock status before displaying add-to-cart buttons — Printful occasionally marks variants out of stock, and ordering an out-of-stock variant causes order failures.
Alternatives
Spocket is a dropshipping marketplace with pre-made products from US/EU suppliers rather than print-on-demand, better for selling existing products without custom design requirements.
Etsy is the sales platform where you list your Printful-powered products for organic discovery — the two are often used together, with Printful fulfilling orders from your Etsy shop.
WooCommerce is a full e-commerce platform with native Printful plugin integration, more appropriate if you want a complete store management system beyond a custom-built Bolt.new storefront.
eBay provides a large marketplace for selling your Printful products, offering an established audience compared to building your own storefront with Bolt.new.
Frequently asked questions
Can I use Printful API in Bolt.new during development without deploying first?
Yes — outbound HTTPS calls to Printful's REST API work fully inside Bolt's WebContainer. You can browse the catalog, test variant data, and even generate mockups (as long as your design images are at publicly accessible URLs). The only features that require deployment are incoming webhooks from Printful for order status updates. Basic catalog, mockup, and order creation work end-to-end in the Bolt preview.
How much does Printful cost to integrate with?
Printful charges no monthly fee and no setup cost. You pay Printful's per-item production cost only when a customer orders. API access is free. Your revenue is the difference between your retail price and Printful's fulfillment cost. For example, Printful might charge you $12 for a t-shirt and you sell it for $25, keeping $13 minus payment processing fees.
Do I need to set up products in the Printful dashboard before using the API?
For creating orders for products with your specific designs, yes — you need to create 'sync products' in your Printful store dashboard where you configure which design goes on which product. For browsing the generic catalog and generating mockups, you can use the raw catalog product IDs and variant IDs without setting up sync products first.
Can I use Printful with my own Stripe payment flow in Bolt?
Yes, and this is the recommended production pattern. Collect payment via Stripe in your Bolt app, confirm payment success via a Stripe webhook on your deployed server, then call the Printful Order API to create the fulfillment order. Never create Printful orders before confirming payment — each Printful order is a real charge to your account. Deploy your app first so Stripe webhooks can reach your server.
How long does Printful take to fulfill and ship orders?
Printful typically fulfills orders in 2-7 business days after order creation, plus shipping time. Standard shipping is 3-7 business days within the US and 10-20 business days internationally. You can offer express shipping by specifying faster shipping methods in the order API call. Printful provides tracking numbers via order status webhooks once items are shipped.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation