To integrate Lovable with Shutterstock, create a Supabase Edge Function that proxies Shutterstock API v2 calls using OAuth2 credentials stored in Cloud → Secrets. The API supports image and video search, licensing, and download. Use Lovable's chat to build media libraries with contributor attribution, license tracking in Supabase, and image search UIs for team stock media workflows.
Build premium stock media libraries with licensing tracking using Shutterstock API v2
Shutterstock's API v2 provides programmatic access to one of the world's largest commercial stock media libraries: over 400 million images, videos, and music tracks, all available for licensing under Shutterstock's standard or enhanced commercial licenses. The API supports search with extensive filtering, image licensing (which generates a download URL and records a license event), and subscription management.
The licensing model is central to the API's design. When you license an image through the Shutterstock API, Shutterstock records the license against your account, issues a download URL for the licensed file, and you can retrieve licensing records later for audit purposes. Building a Lovable integration that tracks these licenses in Supabase creates an internal asset library with full provenance: when was each image licensed, who licensed it, which project it was used in, and what license type was applied.
Shutterstock is positioned between Pixabay (entirely free, community-contributed) and Getty Images (professional editorial, some content only available commercially). Shutterstock offers extensive high-quality commercial stock photography that is appropriate for most marketing, product, and design applications — at a cost. For any use case where you need clean, commercially cleared imagery for client work, advertising, or product packaging, Shutterstock's API is the appropriate licensed source.
Integration method
Shutterstock API v2 uses OAuth2 with client credentials for search and a per-user authorization flow for licensing. OAuth tokens and API credentials must never appear in browser code. All Shutterstock API calls are proxied through a Supabase Edge Function using credentials stored in Cloud → Secrets, with licensing operations stored in Supabase for audit and compliance.
Prerequisites
- A Lovable account with a project that has Lovable Cloud enabled
- A Shutterstock account — a subscription is required for image licensing and download (search and preview are available without a subscription)
- A Shutterstock developer application registered at developers.shutterstock.com
- OAuth2 client ID and client secret from the Shutterstock developer portal
- An active Shutterstock subscription for licensing images, or on-demand download credits
Step-by-step guide
Register a Shutterstock developer application
Register a Shutterstock developer application
Navigate to developers.shutterstock.com and sign in with your Shutterstock account. Click 'Create an application'. Fill in the application name, description, and the OAuth2 callback URL. Use your deployed Lovable app URL followed by /auth/shutterstock/callback — for example, https://your-app.lovable.app/auth/shutterstock/callback. After creating the application, Shutterstock provides a Client ID and Client Secret (also called Consumer Key and Consumer Secret in older documentation). Copy both values. These are the credentials used for both the client credentials flow (for search) and the authorization code flow (for user-specific licensing). Shutterstock's API has two authentication modes. The client credentials flow (two-legged OAuth) gives read access to search results and image metadata without a specific user login. The authorization code flow (three-legged OAuth) authorizes operations on behalf of a logged-in Shutterstock user's account, including licensing images against their subscription. For most Lovable integrations — where the developer manages a single Shutterstock account — the authorization code flow with a long-lived token is the practical approach. To generate a user-level access token, use Shutterstock's OAuth authorization URL: https://www.shutterstock.com/oauth/authorize?client_id={CLIENT_ID}&response_type=code&redirect_uri={REDIRECT_URI}&scope=licenses.create+licenses.view+subscriptions.read After authorizing, exchange the code for an access token with an expiry and refresh token.
Pro tip: Shutterstock access tokens expire. Store the refresh token in Cloud → Secrets and implement token refresh in the Edge Function to avoid manual renewal every time the token expires.
Expected result: You have a Shutterstock Client ID, Client Secret, and an OAuth2 access token with the licenses.create and licenses.view scopes. The token authorizes licensing images against your Shutterstock subscription.
Store Shutterstock credentials in Cloud → Secrets
Store Shutterstock credentials in Cloud → Secrets
Open your Lovable project and navigate to Cloud → Secrets via the '+' icon next to Preview. Add the following secrets: - Name: SHUTTERSTOCK_CLIENT_ID — Value: your Shutterstock application Client ID - Name: SHUTTERSTOCK_CLIENT_SECRET — Value: your Shutterstock application Client Secret - Name: SHUTTERSTOCK_ACCESS_TOKEN — Value: your OAuth2 access token (with licensing scope) - Name: SHUTTERSTOCK_REFRESH_TOKEN — Value: your OAuth2 refresh token The access token authorizes image licensing — actions that consume your Shutterstock subscription credits. This credential requires strong protection. Store it in Cloud → Secrets and never include it in frontend code, Lovable chat, or source code repositories. Shutterstock licensing is irreversible and billable. An exposed access token could be used to license images against your subscription, consuming credits you paid for. Apply extra caution with this credential.
Expected result: All four Shutterstock secrets appear in Cloud → Secrets with masked values.
Create the Shutterstock API proxy Edge Function
Create the Shutterstock API proxy Edge Function
Create a Supabase Edge Function that proxies Shutterstock API v2 calls. The Shutterstock API base URL is https://api.shutterstock.com/v2/ and uses the Authorization: Bearer {token} header. The function handles three core actions: search (search images or videos with filters), license (license a specific image against the account's subscription), and licensed_images (retrieve the account's licensing history). The search action uses the client credentials token for read-only access. The license and licensed_images actions use the user-level access token with the licensing scope. Shutterstock search returns image metadata including preview image URLs (display_sizes array), contributor information, keywords, and image dimensions. The preview URLs do not require authentication and can be displayed in the browser as watermarked thumbnails. The full-resolution licensed download URL is only returned after a successful license call.
Create a Supabase Edge Function at supabase/functions/shutterstock-api/index.ts. Accept POST requests with 'action' (search | license | licensed_images) and 'params'. For 'search': GET https://api.shutterstock.com/v2/images/search?query={q}&image_type={type}&orientation={orientation}&per_page=20 with SHUTTERSTOCK_ACCESS_TOKEN as Bearer. For 'license': POST https://api.shutterstock.com/v2/images/licenses with body { images: [{ image_id, subscription_id }] }. For 'licensed_images': GET https://api.shutterstock.com/v2/images/licenses. Include CORS headers.
Paste this in Lovable chat
1// supabase/functions/shutterstock-api/index.ts2const SHUTTERSTOCK_BASE = 'https://api.shutterstock.com/v2';34Deno.serve(async (req) => {5 if (req.method === 'OPTIONS') {6 return new Response(null, {7 headers: {8 'Access-Control-Allow-Origin': '*',9 'Access-Control-Allow-Methods': 'POST, OPTIONS',10 'Access-Control-Allow-Headers': 'Content-Type, Authorization',11 },12 });13 }1415 const accessToken = Deno.env.get('SHUTTERSTOCK_ACCESS_TOKEN');16 if (!accessToken) {17 return new Response(JSON.stringify({ error: 'Shutterstock credentials not configured' }), {18 status: 500,19 headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },20 });21 }2223 const headers = {24 'Authorization': `Bearer ${accessToken}`,25 'Content-Type': 'application/json',26 };2728 const { action, params } = await req.json();29 let url: string;30 let fetchOptions: RequestInit;3132 if (action === 'search') {33 const q = new URLSearchParams({34 query: params.query || '',35 image_type: params.image_type || 'photo',36 orientation: params.orientation || 'horizontal',37 per_page: params.per_page || '20',38 page: params.page || '1',39 view: 'full',40 });41 if (params.category) q.set('category', params.category);42 url = `${SHUTTERSTOCK_BASE}/images/search?${q}`;43 fetchOptions = { method: 'GET', headers };44 } else if (action === 'license') {45 url = `${SHUTTERSTOCK_BASE}/images/licenses`;46 fetchOptions = {47 method: 'POST',48 headers,49 body: JSON.stringify({50 images: [{51 image_id: params.image_id,52 subscription_id: params.subscription_id,53 format: 'jpg',54 size: 'huge',55 }],56 }),57 };58 } else if (action === 'licensed_images') {59 url = `${SHUTTERSTOCK_BASE}/images/licenses?per_page=100`;60 fetchOptions = { method: 'GET', headers };61 } else if (action === 'subscriptions') {62 url = `${SHUTTERSTOCK_BASE}/subscriptions`;63 fetchOptions = { method: 'GET', headers };64 } else {65 return new Response(JSON.stringify({ error: 'Invalid action' }), {66 status: 400,67 headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },68 });69 }7071 const response = await fetch(url, fetchOptions);72 const data = await response.json();7374 return new Response(JSON.stringify(data), {75 status: response.ok ? 200 : response.status,76 headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },77 });78});Pro tip: Call the 'subscriptions' action first to retrieve your active Shutterstock subscription ID. You need this subscription_id when licensing images — each license call must reference an active subscription.
Expected result: The shutterstock-api Edge Function is deployed. Calling it with { action: 'search', params: { query: 'business' } } returns Shutterstock image results with watermarked preview URLs.
Build the media library with license tracking
Build the media library with license tracking
Use Lovable's chat to build the stock image search and licensing interface. The UI needs three views: search results (watermarked previews with metadata), the licensed assets library (images your account has licensed), and a licensing workflow (preview → confirm → license → store). For the search results view, display Shutterstock's preview images (from display_sizes[0].url in the search response) in a masonry grid. Show the contributor name (contributor.id or contributor.display_name), the main category, and the image dimensions. Add filter controls for image type (photo, illustration, vector) and orientation. For the licensing workflow, clicking an image should open a confirmation modal that shows the image, its Shutterstock ID, the licensing cost from your subscription, and a 'License Image' button. When the user confirms, call the Edge Function with action='license', then save the result to a Supabase 'licensed_assets' table with the image ID, the license ID from Shutterstock's response, the download URL, and the user who licensed it. For the licensed assets library, call the Edge Function with action='licensed_images' and display previously licensed images with their license dates, contributor names, and direct download links. This view serves as your internal asset management system, giving the team access to previously licensed images without re-licensing. For enterprises managing multiple brand accounts and large stock media libraries, RapidDev's team can help design the multi-account architecture and automated license compliance tracking.
Build a stock media library with two tabs: (1) 'Search Shutterstock' showing a search input with orientation and type filters, displaying results as a watermarked thumbnail grid with contributor name and a 'License' button — clicking License opens a confirmation modal that calls the shutterstock-api Edge Function to license the image and saves the result to a Supabase 'licensed_assets' table; (2) 'Licensed Assets' showing images from the licensed_assets table with their license date, contributor, and a download link from the Shutterstock license response.
Paste this in Lovable chat
Expected result: A two-tab media library shows Shutterstock search results with watermarked previews and a licensed assets collection. Licensing an image calls the Edge Function, records the license in Supabase, and adds the image to the licensed assets tab.
Common use cases
Internal stock media library with license tracking
Build an internal tool where a design team searches Shutterstock, previews images with watermarks, licenses selected images, and builds an internal library of licensed assets. Each licensed image is stored in Supabase with its download URL, license ID, and license date for compliance auditing.
Create a stock image library that calls the shutterstock-api Edge Function to search Shutterstock by keyword. Show watermarked preview thumbnails in a grid. Add a 'License Image' button that calls the Edge Function to license the image, saves the license details to a Supabase 'licensed_assets' table with image_id, license_id, licensed_at, licensed_by, and download_url, and downloads the image to Supabase Storage.
Copy this prompt to try it in Lovable
Marketing asset search and download tool
Create a tool for a marketing team where members can search Shutterstock for campaign imagery, filter by orientation and image type, preview options, and batch-license multiple images for a campaign. The tool organizes licensed images into campaign folders in Supabase Storage.
Build a campaign asset search page with a Shutterstock search input, filters for orientation (landscape, portrait, square) and image type (photo, illustration, vector). Show search results with photographer name and keywords. Add checkboxes to select multiple images, then a 'License Selected' button that calls the shutterstock-api Edge Function to license each image and organize them in a Supabase Storage folder named after the active campaign.
Copy this prompt to try it in Lovable
Contributor showcase with image attribution
Build a discovery interface that highlights Shutterstock contributors' work, showing portfolios, contributor names, and collections alongside search results. The interface supports creative teams in finding specific photographers whose style matches their project needs.
Create an image discovery page that calls the shutterstock-api Edge Function with a photographer or keyword search. Show search results grouped by contributor with their display name, total images, and a sample grid of their work. Clicking a contributor opens their full portfolio page with all available images. Add a 'Follow Contributor' feature that saves their contributor ID to Supabase for future reference.
Copy this prompt to try it in Lovable
Troubleshooting
License request returns 403 with 'Insufficient subscription access'
Cause: The access token does not have the licenses.create scope, the Shutterstock subscription associated with the account has expired, or the subscription_id provided does not match an active subscription.
Solution: First, call the subscriptions action on the Edge Function to check your active subscriptions. Verify the subscription_id you are using in the license request matches an active subscription. Then check that your access token was generated with the licenses.create scope by regenerating it with the correct scopes at developers.shutterstock.com.
Search returns 401 Unauthorized with 'Unauthorized access'
Cause: The SHUTTERSTOCK_ACCESS_TOKEN is expired, revoked, or the application's client credentials are invalid.
Solution: Use the stored refresh token to obtain a new access token. Make a POST request to https://api.shutterstock.com/v2/oauth/access_token with grant_type=refresh_token, your refresh_token, client_id, and client_secret. Update SHUTTERSTOCK_ACCESS_TOKEN in Cloud → Secrets with the new token.
1// Token refresh for Shutterstock2const refreshResponse = await fetch('https://api.shutterstock.com/v2/oauth/access_token', {3 method: 'POST',4 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },5 body: new URLSearchParams({6 grant_type: 'refresh_token',7 refresh_token: Deno.env.get('SHUTTERSTOCK_REFRESH_TOKEN')!,8 client_id: Deno.env.get('SHUTTERSTOCK_CLIENT_ID')!,9 client_secret: Deno.env.get('SHUTTERSTOCK_CLIENT_SECRET')!,10 }),11});12const { access_token } = await refreshResponse.json();Licensed image download URL returns 403 or 404 after licensing
Cause: Shutterstock license download URLs are time-limited. They expire after a short window (typically a few minutes to hours) and cannot be reused after expiry.
Solution: Download the licensed image immediately after licensing and store it in Supabase Storage rather than storing the temporary Shutterstock download URL. The download URL in the license response is valid for immediate use only. If you store a Shutterstock download URL and try to use it later, you will need to re-download from the licensing history endpoint which provides a re-download option for previously licensed images.
Best practices
- Store Shutterstock OAuth credentials in Cloud → Secrets — the access token authorizes billable image licensing and requires strict security handling
- Always fetch subscription details before licensing to obtain the correct subscription_id — licensing requests without a valid subscription_id will fail
- Save every license response to a Supabase table immediately after licensing — Shutterstock license records can be retrieved later but storing them locally provides faster access and enables custom compliance reporting
- Download licensed images to Supabase Storage immediately after licensing — Shutterstock download URLs expire and re-downloading later requires going through the license history endpoint
- Display contributor attribution (contributor name, contributor_id, and a link to their Shutterstock portfolio) alongside licensed images as a best practice for respecting creator work
- Implement search result caching in Supabase for popular queries — Shutterstock's API has rate limits and common searches change infrequently, making caching safe and effective
- Separate search (read-only, uses client credentials) from licensing (user action, uses user token with licensing scope) in your Edge Function to apply appropriate access controls
Alternatives
Choose Pixabay API if you need free stock images for your project without licensing fees — Pixabay's community-contributed content is free for commercial use but lacks the professional quality and breadth of Shutterstock's catalog.
Choose Getty Images API if you need editorial photography (news events, celebrities, sports) which is Getty's specialty and is either unavailable or restricted on Shutterstock.
Choose Vimeo if your media needs are video hosting and management for your own content rather than licensing stock images from a commercial library.
Frequently asked questions
Do I need a Shutterstock subscription to use the API?
You need a Shutterstock subscription (or on-demand credits) to license and download images through the API. Search and preview functionality is available with just the API credentials — you can search images and display watermarked previews without a subscription. To actually download licensed images (watermark-free, high resolution), your account must have an active subscription or sufficient on-demand credits.
How does Shutterstock API licensing compare to downloading from the website?
API licensing and website licensing are functionally identical — both consume a license from your subscription, record the license event, and provide a download URL for the high-resolution file. The difference is that API licensing happens programmatically and can be triggered from your Lovable app, while website licensing happens through Shutterstock's interface. Both methods are covered by the same Shutterstock license terms and are legally equivalent for commercial use.
Can I re-download a previously licensed Shutterstock image?
Yes — Shutterstock allows re-downloading of previously licensed images through the licensing history endpoint. Call the licensed_images action on the Edge Function to retrieve past licenses, then call the re-download endpoint for a specific license_id to get a fresh download URL. Re-downloads do not consume additional subscription credits. This is particularly useful when you need to recover images that were not saved to Supabase Storage after the initial licensing.
What is the difference between standard and enhanced Shutterstock licenses?
Shutterstock's standard license covers most digital use cases: websites, apps, social media, digital advertising, and print up to 500,000 copies. The enhanced license covers high-volume print runs over 500,000 copies, merchandise for resale (T-shirts, mugs), and broadcast use in television and film. Most digital product use cases require only the standard license. The API supports requesting enhanced licenses by specifying the license type in the license request, but enhanced licenses cost significantly more per image and require an appropriate subscription tier.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation