Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with Printful

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.

What you'll learn

  • How to get a Printful API key and understand the catalog, mockup, and order APIs
  • How to browse Printful's product catalog and get variant information for your storefront
  • How to generate product mockup images by uploading your design and calling the Printful Mockup Generator API
  • How to create orders programmatically that Printful fulfills and ships to your customers
  • How to build a custom product designer or storefront UI in React using Printful's API data
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read25 minutesE-commerceApril 2026RapidDev Engineering Team
TL;DR

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

Bolt Chat + API Route

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

1

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.

Bolt.new Prompt

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

src/lib/printful.ts
1// src/lib/printful.ts
2const PRINTFUL_BASE = 'https://api.printful.com';
3
4interface PrintfulResponse<T> {
5 code: number;
6 result: T;
7 error?: string;
8}
9
10export 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');
16
17 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 });
25
26 const data: PrintfulResponse<T> = await response.json();
27
28 if (data.code !== 200) {
29 throw new Error(`Printful API error ${data.code}: ${data.error ?? 'Unknown error'}`);
30 }
31
32 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.

2

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.

Bolt.new Prompt

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

app/api/printful/products/route.ts
1// app/api/printful/products/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { printfulFetch } from '@/lib/printful';
4
5interface PrintfulVariant {
6 id: number;
7 price: string;
8 in_stock: boolean;
9}
10
11interface PrintfulProduct {
12 id: number;
13 title: string;
14 image: string;
15 description: string;
16 variants: PrintfulVariant[];
17}
18
19export 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') ?? '';
23
24 const queryParts = [`limit=${limit}`, 'offset=0'];
25 if (categoryId) queryParts.push(`category_id=${categoryId}`);
26
27 const products = await printfulFetch<PrintfulProduct[]>(
28 `products?${queryParts.join('&')}`
29 );
30
31 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.length
37 ? Math.min(...p.variants.map(v => parseFloat(v.price))).toFixed(2)
38 : null,
39 variantCount: p.variants.length,
40 }));
41
42 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.

3

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.

Bolt.new Prompt

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

app/api/printful/mockup/route.ts
1// app/api/printful/mockup/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { printfulFetch } from '@/lib/printful';
4
5interface 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}
14
15export async function POST(request: NextRequest) {
16 const { productId, variantIds, imageUrl } = await request.json();
17
18 try {
19 // Step 1: Create mockup generation task
20 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 );
32
33 const taskKey = task.task_key;
34
35 // 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 3s
38
39 const result = await printfulFetch<MockupTask>(
40 `mockup-generator/task?task_key=${taskKey}`
41 );
42
43 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 }
50
51 if (result.status === 'failed') {
52 return NextResponse.json({ error: 'Mockup generation failed' }, { status: 500 });
53 }
54 }
55
56 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.

4

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.

Bolt.new Prompt

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

app/api/printful/orders/route.ts
1// app/api/printful/orders/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { printfulFetch } from '@/lib/printful';
4
5interface PrintfulOrder {
6 id: number;
7 external_id: string;
8 status: string;
9 shipping: string;
10 created: number;
11}
12
13export async function POST(request: NextRequest) {
14 const { recipient, items, externalId } = await request.json();
15
16 // Validate required fields
17 if (!recipient?.address1 || !recipient?.city || !recipient?.country_code) {
18 return NextResponse.json({ error: 'Complete shipping address required' }, { status: 400 });
19 }
20
21 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 shipping
27 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 customer
41 })),
42 // Use confirm: false during development — creates a draft order without processing
43 confirm: process.env.NODE_ENV === 'production',
44 }),
45 });
46
47 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.

Bolt.new Prompt

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.

Bolt.new Prompt

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.

Bolt.new Prompt

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.

typescript
1// Correct Authorization header format
2headers: {
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

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.