--- title: Manage media for products and collections description: Learn how to manage product and collection media using the GraphQL Admin API. source_url: html: >- https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media md: >- https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md --- ExpandOn this page * [Requirements](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#requirements) * [How it works](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#how-it-works) * [Step 1: Upload files to Shopify](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-1-upload-files-to-shopify) * [Step 2: Poll for file readiness](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-2-poll-for-file-readiness) * [Step 3: Add media to products](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-3-add-media-to-products) * [Step 4: Add media to product variants](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-4-add-media-to-product-variants) * [Step 5: Add images to collections](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-5-add-images-to-collections) * [Step 6: Update and reorder media](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-6-update-and-reorder-media) * [Step 7: Delete files](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-7-delete-files) * [Next steps](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#next-steps) # Manage media for products and collections Use the GraphQL Admin API to manage media for products and collections. The API supports images, Shopify-hosted videos, external videos (YouTube and Vimeo), and 3D models. *** ## Requirements * Your app can make [authenticated requests](https://shopify.dev/docs/api/admin-graphql#authentication) to the latest version of the GraphQL Admin API or higher. * Your app has the `read_products`, `write_products`, and `write_files` [access scopes](https://shopify.dev/docs/api/usage/access-scopes). Learn how to [configure your access scopes using Shopify CLI](https://shopify.dev/docs/apps/build/cli-for-apps/app-configuration). * You've created products to associate media with. If you need to create products first, refer to [Add product data](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/add-data). *** ## How it works The GraphQL Admin API manages files independently from products using a unified file system. Upload a file once to Shopify's CDN using [`fileCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fileCreate) or [`stagedUploadsCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/stagedUploadsCreate), get a unique file ID, then reference that ID from multiple products, variants, collections, or themes. Updates to the file propagate everywhere it's used. Files are processed asynchronously. You poll the `fileStatus` field until it's `READY`, then associate the file with products using the file ID. This separation allows file reuse across resources and prevents duplicate uploads. For example, one product image can be referenced by multiple products without uploading it multiple times. ### Supported media types | Media type | Formats | Size limits | Key details | | - | - | - | - | | [**MediaImage**](https://shopify.dev/docs/api/admin-graphql/latest/objects/mediaimage) | PNG, GIF, JPEG, WEBP, HEIC | Max 20 MB Max 4472 x 4472 px (20 MP) Recommended: 2048 x 2048 px | Shopify generates multiple sizes automatically for responsive display. Aspect ratio: 100:1 to 1:100 | | [**Video**](https://shopify.dev/docs/api/admin-graphql/latest/objects/video) | MP4, MOV, WEBM | Max 1 GB Max 10 minutes Max 3840x2160 (UHD/4K) | Shopify generates multiple resolutions (480p, 720p, 1080p) and formats (HLS, MP4). Apps can create up to 1,000 videos for each store in a week. | | [**ExternalVideo**](https://shopify.dev/docs/api/admin-graphql/latest/objects/externalvideo) | YouTube, Vimeo | No limits (hosted externally) | No file upload required. Provide the embed or share URL. Doesn't count against shop's storage quota. | | [**Model3d**](https://shopify.dev/docs/api/admin-graphql/latest/objects/model3d) | GLB, USDZ | Max 500 MB | For augmented reality on supported devices. Shopify serves models in both GLB (web) and USDZ (iOS AR) formats. | *** ## Step 1: Upload files to Shopify Before you can display media to customers, files must be hosted on Shopify's CDN. Shopify processes files asynchronously, generating multiple sizes for images, transcoding videos to multiple resolutions, and optimizing 3D models. Files are then served globally through a fast CDN. The GraphQL Admin API provides two approaches for uploading files: | Approach | Best for | How it works | | - | - | - | | [`fileCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fileCreate) | Simple uploads from accessible URLs | Provide a URL and Shopify downloads, processes, and stores the file. | | [`stagedUploadsCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/stagedUploadsCreate) | Large files, files on your server, or when you need more control | Get secure upload credentials, upload directly to Shopify's storage, then register the file. | ### Option A: Upload from a URL with `fileCreate` Use [`fileCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fileCreate) when your files are already accessible at a public URL. This is the simplest approach: you provide a URL and Shopify handles downloading, processing, and storing the file. The following example uploads an image from an external URL: ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation CreateFile { fileCreate(files: [{ # The URL where Shopify will download the file from. originalSource: "https://example.com/image.jpg", alt: "Product image", # Specify the media type: IMAGE, VIDEO, or MODEL_3D. contentType: IMAGE }]) { files { id fileStatus alt } userErrors { field message } } } ``` ## JSON response ```json { "data": { "fileCreate": { "files": [ { "id": "gid://shopify/MediaImage/25823372804185", "fileStatus": "PROCESSING", "alt": "Product image" } ], "userErrors": [] } } } ``` ### Option B: Upload directly with `stagedUploadsCreate` Use [`stagedUploadsCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/stagedUploadsCreate) when you need more control over the upload process. This two-step approach is recommended for: * **Large files**: Videos, 3D models, and high-resolution images benefit from direct uploads. * **Files on your server**: Files that aren't accessible via public URL. * **Unreliable networks**: Direct uploads handle interruptions more gracefully. * **Bulk imports**: Uploading many files efficiently. #### 1.​Generate upload credentials Generate secure upload credentials for your files. You must specify the `fileSize` for videos and 3D models: ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation generateStagedUploads { stagedUploadsCreate(input: [ { filename: "watches_comparison.mp4", mimeType: "video/mp4", resource: VIDEO, # fileSize is required for videos and 3D models. fileSize: "899765" }, { filename: "watch_model.glb", mimeType: "model/gltf-binary", resource: MODEL_3D, fileSize: "456000" } ]) { stagedTargets { url resourceUrl parameters { name value } } userErrors { field message } } } ``` ## JSON response ```json { "data": { "stagedUploadsCreate": { "stagedTargets": [ { "url": "https://shopify-video-production-core-originals.storage.googleapis.com", "resourceUrl": "https://shopify-video-production-core-originals.storage.googleapis.com?external_video_id=8490719", "parameters": [ { "name": "GoogleAccessId", "value": "video-production@video-production-225115.iam.gserviceaccount.com" }, { "name": "key", "value": "c/o/v/2e285673bb044aa1a174f813a2e953cf.mp4" } ] }, { "url": "https://storage.googleapis.com/threed-models-production/models/1495b0cb3bcee78e/watch_model.glb", "resourceUrl": "https://storage.googleapis.com/threed-models-production/models/1495b0cb3bcee78e/watch_model.glb?external_model3d_id=bW9kZWwzZC0yNjIzMTA=", "parameters": [ { "name": "GoogleAccessId", "value": "threed-model-service-prod@threed-model-service.iam.gserviceaccount.com" }, { "name": "key", "value": "models/1495b0cb3bcee78e/watch_model.glb" } ] } ], "userErrors": [] } } } ``` #### 2.​Upload the file Upload the file to the `url` using the provided `parameters`. The request format differs by media type. **Videos and 3D models** use a POST request with multipart form data: ```terminal curl -v \ -F "GoogleAccessId=video-production@video-production-225115.iam.gserviceaccount.com" \ -F "key=c/o/v/123bbb4321f4d40a101mi1fd3c32aa7.mp4" \ -F "policy=eyJjb25kaXRpb25zIjpbWyJlcSIsIiRidWNrZXQiLCJzaG9waWZ5..." \ -F "signature=vD7N/vHO4MS0EpG..." \ -F "file=@/path/to/watches_comparison.mp4" \ "https://shopify-video-production-core-originals.storage.googleapis.com" ``` **Images** use a PUT request with parameters as headers: ```terminal curl -X PUT -T /path/to/image.png \ -H 'content_type:image/png' \ -H 'acl:private' \ "https://shopify-staged-uploads.storage.googleapis.com/tmp/45732462614/products/4da01e43-76ff-4014-be67-754bc154494d/image.png?X-Goog-Algorithm=GOOG4-RSA-SHA256&..." ``` Tip You can send requests with form data using API clients like [Postman](https://www.getpostman.com/) or [Insomnia](https://insomnia.rest/). #### 3.​Register the uploaded file After uploading, register the file with Shopify using [`fileCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fileCreate). Use the `resourceUrl` from the staged upload response as the `originalSource`: ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation CreateFileFromStagedUpload { fileCreate(files: [{ # Use the resourceUrl from the stagedUploadsCreate response. originalSource: "https://shopify-video-production-core-originals.storage.googleapis.com?external_video_id=8490719", alt: "Product comparison video", contentType: VIDEO }]) { files { id fileStatus alt } userErrors { field message } } } ``` ## JSON response ```json { "data": { "fileCreate": { "files": [ { "id": "gid://shopify/Video/25823372804185", "fileStatus": "PROCESSING", "alt": "Product comparison video" } ], "userErrors": [] } } } ``` *** ## Step 2: Poll for file readiness Regardless of which upload method you use, files are processed asynchronously. Poll the `fileStatus` field until it becomes `READY` before associating the file with products: * `UPLOADED`: File has been received by Shopify's servers. * `PROCESSING`: File is being processed (resized, transcoded, and so on). * `READY`: File is ready to associate with products. * `FAILED`: Processing failed. Check error details. ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL query ```graphql query CheckFileStatus { # Use the file ID returned from fileCreate. node(id: "gid://shopify/MediaImage/25823372804185") { ... on File { # Poll until fileStatus is READY. fileStatus preview { image { url } } } } } ``` ## JSON response (when READY) ```json { "data": { "node": { "fileStatus": "READY", "preview": { "image": { "url": "https://cdn.shopify.com/s/files/1/0574/7248/3417/files/image.jpg" } } } } } ``` *** ## Step 3: Add media to products After a file's status is `READY`, you can associate it with products to display the media to customers. This association determines which media appears on product pages, in collection listings, and across sales channels. Use [`productSet`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productSet), [`productCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productCreate), or [`productUpdate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productUpdate) with the `media` field. Reference the file by its ID in the `originalSource` field. ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation AssociateMediaWithProduct { productSet(input: { id: "gid://shopify/Product/7882412064857", files: [ { # Reference the file by its ID (not a URL). originalSource: "gid://shopify/MediaImage/25823372804185", alt: "Product image", contentType: IMAGE } ] }) { product { id media(first: 10) { nodes { alt mediaContentType ... on MediaImage { image { url } } } } } userErrors { field message } } } ``` ## JSON response ```json { "data": { "productSet": { "product": { "id": "gid://shopify/Product/7882412064857", "media": { "nodes": [ { "alt": "Product image", "mediaContentType": "IMAGE", "image": { "url": "https://cdn.shopify.com/s/files/1/0574/7248/3417/files/image.jpg" } } ] } }, "userErrors": [] } } } ``` *** ## Step 4: Add media to product variants You can add media to variants when creating products by using the `mediaSrc` field in [`productCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productCreate). The `mediaSrc` value must match one of the media `originalSource` fields on the product. The following example shows how to add media when creating a product with variants: ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation CreateProductWithVariantMedia { productCreate( # Define the media files for the product. media: [ { mediaContentType: IMAGE, originalSource: "https://example.com/red_t_shirt.jpg" }, { mediaContentType: IMAGE, originalSource: "https://example.com/yellow_t_shirt.jpg" } ], product: { title: "T-shirt", productOptions: [ { name: "Color", values: [{ name: "Red" }, { name: "Yellow" }] }, { name: "Size", values: [{ name: "M" }, { name: "L" }] } ], variants: [ { # mediaSrc must match an originalSource value above. mediaSrc: ["https://example.com/red_t_shirt.jpg"], optionValues: [ { optionName: "Color", name: "Red" }, { optionName: "Size", name: "M" } ] }, { mediaSrc: ["https://example.com/red_t_shirt.jpg"], optionValues: [ { optionName: "Color", name: "Red" }, { optionName: "Size", name: "L" } ] }, { mediaSrc: ["https://example.com/yellow_t_shirt.jpg"], optionValues: [ { optionName: "Color", name: "Yellow" }, { optionName: "Size", name: "M" } ] }, { mediaSrc: ["https://example.com/yellow_t_shirt.jpg"], optionValues: [ { optionName: "Color", name: "Yellow" }, { optionName: "Size", name: "L" } ] } ] } ) { product { id variants(first: 10) { edges { node { selectedOptions { name value } media(first: 10) { edges { node { ... on MediaImage { id alt image { url } } } } } } } } } userErrors { field message } } } ``` ## JSON response ```json { "data": { "productCreate": { "product": { "id": "gid://shopify/Product/123456789", "variants": { "edges": [ { "node": { "selectedOptions": [ { "name": "Color", "value": "Red" }, { "name": "Size", "value": "M" } ], "media": { "edges": [ { "node": { "id": "gid://shopify/MediaImage/1", "alt": null, "image": { "url": "https://cdn.shopify.com/s/files/1/0574/7248/3417/files/red_t_shirt.jpg" } } } ] } } }, { "node": { "selectedOptions": [ { "name": "Color", "value": "Red" }, { "name": "Size", "value": "L" } ], "media": { "edges": [ { "node": { "id": "gid://shopify/MediaImage/1", "alt": null, "image": { "url": "https://cdn.shopify.com/s/files/1/0574/7248/3417/files/red_t_shirt.jpg" } } } ] } } } ] } }, "userErrors": [] } } } ``` *** ## Step 5: Add images to collections Collection images appear in collection listings, navigation menus, and promotional banners. Unlike products, collections use image URLs directly instead of file ID references. To add images to collections, use [`collectionCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/collectionCreate) or [`collectionUpdate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/collectionUpdate) with image URLs. You can use Shopify CDN URLs from uploaded files or external URLs from your own hosting. ### Option A: Use Shopify CDN files To add a file from Shopify's CDN to a collection, first upload it with [`fileCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fileCreate), get the CDN URL from the file, then use that URL in the collection mutation. First, query the file to get its CDN URL: ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL query ```graphql query GetFileCDNUrl { # Use the file ID from fileCreate. node(id: "gid://shopify/MediaImage/25823372836953") { id ... on MediaImage { preview { status image { # Use this URL in the collection mutation. url } } } } } ``` ## JSON response ```json { "data": { "node": { "id": "gid://shopify/MediaImage/25823372836953", "preview": { "status": "READY", "image": { "url": "https://cdn.shopify.com/s/files/1/0574/7248/3417/files/myfilename.jpg" } } } } } ``` Then create the collection with the image URL: ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation CreateCollectionWithImage { collectionCreate(input: { title: "My Collection" image: { # Use the CDN URL from the GetFileCDNUrl query. src: "https://cdn.shopify.com/s/files/1/0574/7248/3417/files/myfilename.jpg" altText: "Collection image" } }) { collection { id image { url altText } } userErrors { field message } } } ``` ## JSON response ```json { "data": { "collectionCreate": { "collection": { "id": "gid://shopify/Collection/123", "image": { "url": "https://cdn.shopify.com/s/files/1/0574/7248/3417/files/myfilename.jpg", "altText": "Collection image" } }, "userErrors": [] } } } ``` ### Option B: Use external image URLs You can also provide external URLs directly without uploading to Shopify. This is useful when you host images on your own CDN or need to update images frequently without re-uploading. ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation CreateCollectionWithExternalImage { collectionCreate(input: { title: "My Collection" image: { src: "https://example.com/collection-image.jpg" altText: "Collection image" } }) { collection { id image { url altText } } userErrors { field message } } } ``` ## JSON response ```json { "data": { "collectionCreate": { "collection": { "id": "gid://shopify/Collection/456", "image": { "url": "https://example.com/collection-image.jpg", "altText": "Collection image" } }, "userErrors": [] } } } ``` *** ## Step 6: Update and reorder media After adding media to products, you might need to update which products reference a file or change the display order. ### Update file associations The unified file system's key advantage is file reuse. When you have a logo, brand image, or shared product shot, you can use the same file across multiple products without uploading duplicates. Use [`fileUpdate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fileUpdate) to manage which products reference a file. The following example shows how to move a file from one product to another: ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation UpdateFileAssociations { fileUpdate(files: [{ # The file ID to update associations for. id: "gid://shopify/MediaImage/1234567890", # Remove the file from this product. referencesToRemove: ["gid://shopify/Product/1234567890"], # Add the file to this product. referencesToAdd: ["gid://shopify/Product/9876543210"] }]) { files { id } userErrors { field message } } } ``` ## JSON response ```json { "data": { "fileUpdate": { "files": [ { "id": "gid://shopify/MediaImage/1234567890" } ], "userErrors": [] } } } ``` ### Reorder product media The first media item in a product typically serves as the primary image in collection pages, search results, and product thumbnails. Use [`productReorderMedia`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productreordermedia) to change media order without deleting and re-adding items. ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation reorderProductMedia { productReorderMedia( id: "gid://shopify/Product/1", # Specify new positions for the media you're moving. moves: [ { id: "gid://shopify/MediaImage/37", # Position 0 becomes the primary image. newPosition: "0" }, { id: "gid://shopify/Video/2", newPosition: "1" }, { id: "gid://shopify/ExternalVideo/1", newPosition: "2" } ]) { job { id done } mediaUserErrors { code field message } } } ``` ## JSON response ```json { "data": { "productReorderMedia": { "job": { "id": "gid://shopify/Job/17366d70-740a-4048-a102-82267e30c92a", "done": false }, "mediaUserErrors": [] } } } ``` ### Update collection images To update a collection's image, use [`collectionUpdate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/collectionUpdate) with the new image URL: ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation UpdateCollectionImage { collectionUpdate(input: { id: "gid://shopify/Collection/1234567890" image: { src: "https://cdn.shopify.com/s/files/1/0574/7248/3417/files/myfilename.jpg" altText: "Updated collection image" } }) { collection { id image { url altText } } userErrors { field message } } } ``` ## JSON response ```json { "data": { "collectionUpdate": { "collection": { "id": "gid://shopify/Collection/1234567890", "image": { "url": "https://cdn.shopify.com/s/files/1/0574/7248/3417/files/myfilename.jpg", "altText": "Updated collection image" } }, "userErrors": [] } } } ``` *** ## Step 7: Delete files Use [`fileDelete`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fileDelete) to permanently remove files from your shop. This is useful for removing discontinued product images, cleaning up test files, or freeing storage space. Caution This permanently deletes the file and all its associations with products, variants, and other resources. Any products referencing the deleted file will have broken media references. ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## GraphQL mutation ```graphql mutation DeleteFile { # Pass one or more file IDs to delete. fileDelete(fileIds: ["gid://shopify/MediaImage/1234567890"]) { deletedFileIds userErrors { field message } } } ``` ## JSON response ```json { "data": { "fileDelete": { "deletedFileIds": [ "gid://shopify/MediaImage/1234567890" ], "userErrors": [] } } } ``` *** ## Next steps * Learn how to [add product data](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/add-data). * Learn how to [update product data](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/update-data). * Explore the [`fileCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fileCreate) mutation in the GraphQL Admin API reference. *** * [Requirements](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#requirements) * [How it works](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#how-it-works) * [Step 1: Upload files to Shopify](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-1-upload-files-to-shopify) * [Step 2: Poll for file readiness](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-2-poll-for-file-readiness) * [Step 3: Add media to products](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-3-add-media-to-products) * [Step 4: Add media to product variants](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-4-add-media-to-product-variants) * [Step 5: Add images to collections](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-5-add-images-to-collections) * [Step 6: Update and reorder media](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-6-update-and-reorder-media) * [Step 7: Delete files](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#step-7-delete-files) * [Next steps](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections/manage-media.md#next-steps)