Device Mockup API Reference: Parameters, Automation & CI/CD Integration
Technical reference for the Screenshotly device mockup API. Parameter documentation, batch automation, GitHub Actions integration, and programmatic mockup selection.
Developers who need to generate device mockups at scale—app store screenshots, documentation assets, or visual regression suites—often struggle to find a consolidated API reference. This guide covers the Screenshotly device mockup API from a technical perspective: every parameter, automation patterns, and CI/CD integration.
If you want to automate mockup generation in pipelines, batch thousands of captures, or programmatically select mockup frames, this is the reference.
API Overview
The Screenshotly screenshot endpoint accepts a mockup parameter (device frame identifier) and optional mockupOptions for styling. Mockups are applied as post-processing overlays; the underlying screenshot resolution is preserved.
Base request structure:
const response = await fetch('https://api.screenshotly.app/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.SCREENSHOTLY_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com',
mockup: 'browser-light', // Frame identifier
mockupOptions: { // Optional styling
shadow: true,
background: 'white',
},
format: 'png',
}),
});
Complete Parameter Reference
mockup (required for mockup output)
The frame identifier. Case-sensitive. Valid values by category:
| Category | Identifiers |
|---|---|
| Browsers | browser-light, browser-dark, browser-minimal, safari-light, safari-dark, chrome-light, chrome-dark |
| Phones | iphone-15-pro, iphone-15-pro-max, iphone-15, iphone-14-pro, iphone-14, iphone-13, android-generic |
| Tablets | ipad-pro-12.9, ipad-pro-11, ipad-air, ipad-mini |
| Laptops | macbook-pro, macbook-air, laptop-generic |
| Other | desktop-monitor, minimal-frame-light, minimal-frame-dark |
mockupOptions (optional object)
| Parameter | Type | Default | Description |
|---|---|---|---|
color |
string | device default | Device color variant. Apple: space-black, natural-titanium, blue, white, silver, space-gray, starlight, midnight, gold. Laptops: space-gray, silver, gold. |
orientation |
string | portrait |
portrait or landscape. Applies to phones and tablets. |
shadow |
boolean | true |
Enable drop shadow under the device/frame. |
background |
string | transparent |
Background behind mockup: transparent, white, black, or hex (e.g. #f5f5f5). PNG required for transparency. |
showUrl |
boolean | true |
For browser frames: show URL in the address bar. |
showControls |
boolean | true |
For browser frames: show traffic-light / minimize/maximize buttons. |
padding |
object | auto | { top, bottom, left, right } in pixels. Controls spacing around the mockup. |
Device ↔ Viewport Mapping
| Device Type | Recommended device |
Typical Viewport |
|---|---|---|
| Phone mockup | mobile or iphone-14-pro |
390×844 or 430×932 |
| Tablet mockup | tablet or ipad-pro-12.9 |
1024×1366 (portrait) or 1366×1024 (landscape) |
| Laptop/Browser | desktop |
1440×900, 1920×1080, or custom |
Use viewport: { width, height } for pixel-perfect control when the default device presets don't match your target frame.
Batch Mockup Generation
Generate multiple mockups in parallel with controlled concurrency to avoid rate limits.
async function batchMockups(urls, baseOptions = {}) {
const BATCH_SIZE = 5; // Concurrent requests
const results = [];
for (let i = 0; i < urls.length; i += BATCH_SIZE) {
const batch = urls.slice(i, i + BATCH_SIZE);
const batchResults = await Promise.all(
batch.map(url => captureWithMockup(url, baseOptions))
);
results.push(...batchResults);
}
return results;
}
async function captureWithMockup(url, options) {
const response = await fetch('https://api.screenshotly.app/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.SCREENSHOTLY_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url,
device: options.device || 'desktop',
format: 'png',
mockup: options.mockup || 'browser-light',
mockupOptions: options.mockupOptions || { shadow: true },
...options,
}),
});
if (!response.ok) throw new Error(`Failed: ${url} (${response.status})`);
return { url, buffer: await response.arrayBuffer() };
}
// Example: 10 URLs, MacBook mockup
const urls = ['https://app.com/page1', 'https://app.com/page2', /* ... */];
const images = await batchMockups(urls, {
mockup: 'macbook-pro',
mockupOptions: { color: 'space-gray', shadow: true },
});
Batch with Multiple Mockup Variants
Capture the same URL with different mockup frames for A/B testing or asset variants:
async function generateMockupVariants(url, mockupIds) {
return Promise.all(
mockupIds.map(async (mockupId) => {
const response = await fetch('https://api.screenshotly.app/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.SCREENSHOTLY_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url,
device: mockupId.startsWith('iphone') ? 'mobile' : 'desktop',
format: 'png',
mockup: mockupId,
mockupOptions: { shadow: true, background: 'transparent' },
}),
});
return { mockupId, buffer: await response.arrayBuffer() };
})
);
}
// Output: browser-light, browser-dark, iphone-15-pro, macbook-pro
const variants = await generateMockupVariants('https://app.com', [
'browser-light',
'browser-dark',
'iphone-15-pro',
'macbook-pro',
]);
CI/CD Integration: GitHub Actions
Automate mockup generation on every deploy or release. Example: generate app store screenshots when the app’s marketing pages change.
# .github/workflows/screenshots.yml
name: Generate Screenshots
on:
push:
paths:
- 'app/marketing/**'
- 'landing/**'
workflow_dispatch:
jobs:
screenshots:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate mockup screenshots
env:
SCREENSHOTLY_API_KEY: ${{ secrets.SCREENSHOTLY_API_KEY }}
run: |
npm run generate:screenshots
- name: Upload screenshots artifact
uses: actions/upload-artifact@v4
with:
name: mockup-screenshots
path: output/screenshots/
generate:screenshots script (Node.js):
// scripts/generate-screenshots.js
const fs = require('fs');
const path = require('path');
const PAGES = [
{ url: 'https://staging.myapp.com/features', mockup: 'macbook-pro' },
{ url: 'https://staging.myapp.com/mobile', mockup: 'iphone-15-pro' },
{ url: 'https://staging.myapp.com/dashboard', mockup: 'browser-dark' },
];
async function main() {
const outputDir = path.join(process.cwd(), 'output', 'screenshots');
fs.mkdirSync(outputDir, { recursive: true });
for (const { url, mockup } of PAGES) {
const res = await fetch('https://api.screenshotly.app/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.SCREENSHOTLY_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url,
device: mockup.includes('iphone') ? 'mobile' : 'desktop',
format: 'png',
mockup,
mockupOptions: { shadow: true, background: 'transparent' },
}),
});
const slug = url.split('/').slice(-1)[0] || 'home';
const filename = `${slug}-${mockup}.png`;
fs.writeFileSync(
path.join(outputDir, filename),
Buffer.from(await res.arrayBuffer())
);
console.log(`Saved ${filename}`);
}
}
main().catch(console.error);
Run with node scripts/generate-screenshots.js. Add "generate:screenshots": "node scripts/generate-screenshots.js" to package.json scripts.
Automated App Store Screenshot Generation
App stores require specific resolutions per device. Map device slots to mockup + viewport, then batch-generate.
const APP_STORE_SPECS = {
'iphone-6.7': {
mockup: 'iphone-15-pro-max',
viewport: { width: 430, height: 932 },
},
'iphone-6.5': {
mockup: 'iphone-14-pro-max',
viewport: { width: 428, height: 926 },
},
'iphone-5.5': {
mockup: 'iphone-14',
viewport: { width: 414, height: 736 },
},
'ipad-12.9': {
mockup: 'ipad-pro-12.9',
viewport: { width: 1024, height: 1366 },
},
};
async function generateAppStoreScreenshots(screenUrls, options = {}) {
const results = {};
for (const [deviceSlot, spec] of Object.entries(APP_STORE_SPECS)) {
results[deviceSlot] = [];
for (let i = 0; i < screenUrls.length; i++) {
const url = screenUrls[i];
const response = await fetch('https://api.screenshotly.app/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.SCREENSHOTLY_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url,
viewport: spec.viewport,
format: 'png',
mockup: spec.mockup,
mockupOptions: {
shadow: false,
background: 'transparent',
...options.mockupOptions,
},
...options,
}),
});
results[deviceSlot].push({
index: i,
buffer: await response.arrayBuffer(),
});
}
}
return results;
}
// Usage
const screens = [
'https://app.com/screen1',
'https://app.com/screen2',
'https://app.com/screen3',
];
const assets = await generateAppStoreScreenshots(screens);
// assets['iphone-6.7'] = [buffer0, buffer1, buffer2], etc.
Dynamic Mockup Selection
Choose mockup and options based on device detection, page type, or feature flags.
function selectMockup(userAgent, pageType) {
// Detect mobile from User-Agent
const isMobile = /iPhone|Android|Mobile/i.test(userAgent || '');
if (pageType === 'dashboard') {
return { mockup: 'browser-dark', device: 'desktop' };
}
if (pageType === 'landing') {
return isMobile
? { mockup: 'iphone-15-pro', device: 'mobile' }
: { mockup: 'macbook-pro', device: 'desktop' };
}
return { mockup: 'browser-light', device: 'desktop' };
}
// In your OG image or screenshot route
export async function GET(request) {
const url = new URL(request.url).searchParams.get('url');
const pageType = request.headers.get('x-page-type') || 'general';
const userAgent = request.headers.get('user-agent');
const { mockup, device } = selectMockup(userAgent, pageType);
const response = await fetch('https://api.screenshotly.app/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.SCREENSHOTLY_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url,
device,
mockup,
viewport: { width: 1200, height: 630 },
format: 'png',
mockupOptions: { shadow: true, background: '#f8f9fa' },
}),
});
return new Response(await response.arrayBuffer(), {
headers: { 'Content-Type': 'image/png' },
});
}
Testing Mockup Frames Programmatically
Use a test suite to ensure each mockup type renders without errors and meets minimum size expectations.
// test/mockup-frames.test.js
const MOCKUP_IDS = [
'browser-light',
'browser-dark',
'iphone-15-pro',
'macbook-pro',
'ipad-pro-12.9',
];
describe('Mockup API', () => {
for (const mockupId of MOCKUP_IDS) {
it(`renders ${mockupId} without error`, async () => {
const res = await fetch('https://api.screenshotly.app/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.SCREENSHOTLY_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com',
device: mockupId.includes('iphone') ? 'mobile' : 'desktop',
format: 'png',
mockup: mockupId,
mockupOptions: { shadow: false, background: 'white' },
}),
});
expect(res.ok).toBe(true);
const buffer = await res.arrayBuffer();
expect(buffer.byteLength).toBeGreaterThan(10000);
// Optionally validate PNG header
expect(new Uint8Array(buffer)[0]).toBe(0x89);
expect(new Uint8Array(buffer)[1]).toBe(0x50);
});
}
});
Run with Jest or similar. Add SCREENSHOTLY_API_KEY to your test environment.
Rate Limits and Retries
For batch and CI workflows, implement retries with exponential backoff:
async function captureWithRetry(params, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const res = await fetch('https://api.screenshotly.app/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.SCREENSHOTLY_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
});
if (res.ok) return res.arrayBuffer();
if (res.status === 429) {
const delay = Math.pow(2, attempt) * 1000;
await new Promise(r => setTimeout(r, delay));
continue;
}
throw new Error(`Screenshot failed: ${res.status}`);
}
throw new Error('Max retries exceeded');
}
Summary: Parameter Quick Reference
| Parameter | Location | Purpose |
|---|---|---|
mockup |
top-level | Frame ID (browser-light, iphone-15-pro, etc.) |
mockupOptions.color |
object | Device color variant |
mockupOptions.orientation |
object | portrait / landscape |
mockupOptions.shadow |
object | Drop shadow on/off |
mockupOptions.background |
object | transparent, white, hex |
mockupOptions.showUrl |
object | Browser address bar |
mockupOptions.showControls |
object | Browser window controls |
viewport |
top-level | Screenshot dimensions before mockup |
device |
top-level | Preset viewport (desktop, mobile, tablet) |
Combine these with the batch and CI patterns above to automate mockup generation across your pipeline.
Need mockups in your pipeline?
Get your API key → — 100 free screenshots. Try parameters live in the API Playground → or see the full API docs →.
About the Author

Asad Ali
Full-Stack Developer and Founder of ZTabs with 8+ years of experience building scalable web applications and APIs. Specializes in performance optimization, SaaS development, and modern web technologies.
Credentials: Founder & CEO at ZTabs, Full-Stack Developer, Expert in Next.js, React, Node.js, and API optimization
Ready to capture your first screenshot?
Get started with 100 free screenshots. No credit card required.