Export a Slide Deck to PDF or PNG
Export a slide presentation to PDF or PNG by locking the viewport to the slide's aspect ratio and forcing screen media so backgrounds and fonts render as intended.
- A Browserless API token from your account dashboard
emulateMedia: screen?PDF generation defaults to print media, which strips background colors, changes fonts, and removes visual styling that slide decks rely on. Setting media to screen captures the presentation exactly as it appears in a browser.
Steps
- REST API
- Frameworks
- BQL
Send a POST to the /pdf endpoint with screen media emulation and a 16:9 viewport. The viewport dimensions (1280×720) match the standard slide aspect ratio so each page in the PDF maps to exactly one slide.
- cURL
- JavaScript
- Python
- Java
- C#
1. Build the request
Append your token to the PDF endpoint:
https://production-sfo.browserless.io/pdf?token=YOUR_API_TOKEN_HERE
2. Send the request
curl -X POST \
"https://production-sfo.browserless.io/pdf?token=YOUR_API_TOKEN_HERE" \
-H "Cache-Control: no-cache" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/my-presentation",
"emulateMedia": "screen",
"viewport": { "width": 1280, "height": 720 },
"options": {
"printBackground": true,
"width": "1280px",
"height": "720px"
}
}' \
-o slides.pdf
3. Check the output
The response body is a raw PDF binary written to slides.pdf. Each page is 1280×720px, one slide per page.
Replace the endpoint with /screenshot and remove the options field to capture the first slide as a PNG instead:
curl -X POST \
"https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/my-presentation",
"emulateMedia": "screen",
"viewport": { "width": 1280, "height": 720 },
"options": { "type": "png" }
}' \
-o slide-1.png
1. Send the request
import fs from 'fs';
const response = await fetch(
'https://production-sfo.browserless.io/pdf?token=YOUR_API_TOKEN_HERE',
{
method: 'POST',
headers: {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com/my-presentation',
emulateMedia: 'screen',
viewport: { width: 1280, height: 720 },
options: {
printBackground: true,
width: '1280px',
height: '720px',
},
}),
}
);
const buffer = await response.arrayBuffer();
fs.writeFileSync('slides.pdf', Buffer.from(buffer));
2. Check the output
Run with node export-slide-deck.mjs. You'll find slides.pdf in the current directory with one slide per page.
1. Install dependencies
pip install requests
2. Send the request
import requests
response = requests.post(
'https://production-sfo.browserless.io/pdf?token=YOUR_API_TOKEN_HERE',
headers={'Cache-Control': 'no-cache', 'Content-Type': 'application/json'},
json={
'url': 'https://example.com/my-presentation',
'emulateMedia': 'screen',
'viewport': {'width': 1280, 'height': 720},
'options': {
'printBackground': True,
'width': '1280px',
'height': '720px',
},
},
)
with open('slides.pdf', 'wb') as f:
f.write(response.content)
3. Check the output
Run with python export_slide_deck.py. You'll find slides.pdf in the current directory.
1. Send the request
import java.io.*;
import java.net.URI;
import java.net.http.*;
String token = "YOUR_API_TOKEN_HERE";
String endpoint = "https://production-sfo.browserless.io/pdf?token=" + token;
String payload = """
{
"url": "https://example.com/my-presentation",
"emulateMedia": "screen",
"viewport": { "width": 1280, "height": 720 },
"options": {
"printBackground": true,
"width": "1280px",
"height": "720px"
}
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(endpoint))
.header("Cache-Control", "no-cache")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
try (FileOutputStream fos = new FileOutputStream("slides.pdf")) {
response.body().transferTo(fos);
}
System.out.println("PDF saved as slides.pdf");
2. Check the output
Run the class. You'll find slides.pdf in the current directory.
1. Send the request
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
string token = "YOUR_API_TOKEN_HERE";
string endpoint = $"https://production-sfo.browserless.io/pdf?token={token}";
var payload = new
{
url = "https://example.com/my-presentation",
emulateMedia = "screen",
viewport = new { width = 1280, height = 720 },
options = new
{
printBackground = true,
width = "1280px",
height = "720px",
},
};
using (HttpClient httpClient = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, endpoint);
request.Headers.Add("Cache-Control", "no-cache");
request.Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await httpClient.SendAsync(request);
var contentStream = await response.Content.ReadAsStreamAsync();
using (var fileStream = new FileStream("slides.pdf", FileMode.Create, FileAccess.Write))
{
await contentStream.CopyToAsync(fileStream);
}
Console.WriteLine("PDF saved as slides.pdf");
}
2. Check the output
Run the program. You'll find slides.pdf in the current directory.
Use a direct browser connection when you need control over timing, for example when waiting for CSS animations or JavaScript transitions to finish before capturing. This also lets you produce both PDF and PNG from a single browser session without opening the URL twice.
- Puppeteer
- Playwright
1. Install dependencies
npm install puppeteer-core
2. Connect and export
import puppeteer from 'puppeteer-core';
import fs from 'fs';
const browser = await puppeteer.connect({
browserWSEndpoint: 'wss://production-sfo.browserless.io?token=YOUR_API_TOKEN_HERE',
});
try {
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 720 });
await page.emulateMedia('screen');
await page.goto('https://example.com/my-presentation', {
waitUntil: 'networkidle0',
});
// Export full deck as PDF
const pdf = await page.pdf({
printBackground: true,
width: '1280px',
height: '720px',
});
fs.writeFileSync('slides.pdf', pdf);
// Export first slide as PNG
const png = await page.screenshot({ type: 'png' });
fs.writeFileSync('slide-1.png', png);
} finally {
await browser.close();
}
3. Check the output
Run with node export-slide-deck.mjs. Both slides.pdf and slide-1.png will appear in the current directory.
1. Install dependencies
npm install playwright-core
2. Connect and export
import { chromium } from 'playwright-core';
import fs from 'fs';
const browser = await chromium.connect(
'wss://production-sfo.browserless.io/chromium/playwright?token=YOUR_API_TOKEN_HERE'
);
try {
const page = await browser.newPage();
await page.setViewportSize({ width: 1280, height: 720 });
await page.emulateMedia({ media: 'screen' });
await page.goto('https://example.com/my-presentation', {
waitUntil: 'networkidle',
});
// Export full deck as PDF
const pdf = await page.pdf({
printBackground: true,
width: '1280px',
height: '720px',
});
fs.writeFileSync('slides.pdf', pdf);
// Export first slide as PNG
const png = await page.screenshot({ type: 'png' });
fs.writeFileSync('slide-1.png', png);
} finally {
await browser.close();
}
3. Check the output
Run with node export-slide-deck.mjs. Both slides.pdf and slide-1.png will appear in the current directory.
1. Write the mutation
Navigate to the presentation and export it as a base64-encoded PDF:
mutation ExportSlideDeck {
goto(url: "https://example.com/my-presentation", waitUntil: networkIdle) {
status
}
pdf(printBackground: true, width: "1280px", height: "720px") {
base64
}
}
2. Run it
Paste into the BQL IDE and click Run.
3. Check the output
{
"data": {
"goto": { "status": 200 },
"pdf": { "base64": "JVBERi0xLjQ..." }
}
}
BQL returns the PDF as a base64 string rather than raw binary because the response is JSON. Decode it and write the bytes to a file to get the final PDF.
Next steps
- Generate a PDF — export any webpage as a PDF without slide-specific configuration
- Take a Screenshot — capture a page as a PNG or JPEG
- Export Pages and Assets — download a full page snapshot with all linked assets