curl --request GET \
--url https://api.runpulse.com/results/{jobId}/images/{filename} \
--header 'x-api-key: <api-key>'import requests
url = "https://api.runpulse.com/results/{jobId}/images/{filename}"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://api.runpulse.com/results/{jobId}/images/{filename}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.runpulse.com/results/{jobId}/images/{filename}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.runpulse.com/results/{jobId}/images/{filename}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.runpulse.com/results/{jobId}/images/{filename}")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.runpulse.com/results/{jobId}/images/{filename}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body"<string>"Get Result Image
Stream a PNG/JPEG visual image referenced by an extraction
response under bounding_boxes.Images[].image_url.
The URL is API-hosted instead of raw S3 — the underlying object
store is intentionally not part of the public contract. The host
in image_url mirrors the request origin (e.g. a request to a
beta deployment returns image URLs on that same host).
Authentication is required. Unlike single-use result download
links, visual artifacts are
independently-addressable resources — every fetch must present a
valid API key for the owning org. There is no anonymous /
TTL-based fallback. Use the same x-api-key header you use for
/extract.
Fetching an image does not consume the parent extraction’s result-delivery slot, so one extraction can produce many image URLs and each can be fetched repeatedly while the artifact is retained.
curl --request GET \
--url https://api.runpulse.com/results/{jobId}/images/{filename} \
--header 'x-api-key: <api-key>'import requests
url = "https://api.runpulse.com/results/{jobId}/images/{filename}"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://api.runpulse.com/results/{jobId}/images/{filename}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.runpulse.com/results/{jobId}/images/{filename}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.runpulse.com/results/{jobId}/images/{filename}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.runpulse.com/results/{jobId}/images/{filename}")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.runpulse.com/results/{jobId}/images/{filename}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body"<string>"Overview
Fetch a PNG or JPEG visual image referenced by an extraction response underbounding_boxes.Images[].image_url.
When you call /extract with figure_processing.show_images: true, every detected chart or embedded image in the response carries an image_url field. Those URLs point at this endpoint — GET /results/{jobId}/images/{filename} — which streams the actual image bytes.
{
"bounding_boxes": {
"Images": [
{
"id": "excel_image_1_1",
"visual_type": "chart",
"image_url": "https://api.runpulse.com/results/13e3e75f-.../images/excel_image_1_1.png",
"chart_type": "BarChart",
"chart_title": "Revenue",
"excel_range": "D2",
"sheet_name": "Charts"
}
]
}
}
When to use this vs. /large_results/{jobId}
| Endpoint | Purpose | Auth | Single-use? |
|---|---|---|---|
GET /large_results/{jobId} | Download the full extraction result (markdown + bounding_boxes + …) when the inline payload exceeds 5 MB or 70 pages. | Anonymous within 1-hour TTL or authenticated same-org. | Yes — fetching invalidates the link. |
GET /results/{jobId}/images/{filename} | Download one visual image referenced by bounding_boxes.Images[].image_url. | Authenticated same-org only. No anonymous access. | No — fetch as many times as you need while the artifact is retained. |
End-to-End Example
The full path: extract a workbook → walk the typedImages array → fetch one chart’s bytes.
from pulse import Pulse
from pulse.types import ExtractRequestFigureProcessing
client = Pulse(api_key="YOUR_API_KEY")
# 1) Extract a workbook and ask for image URLs.
response = client.extract(
file=open("financials.xlsx", "rb"),
figure_processing=ExtractRequestFigureProcessing(
show_images=True,
description=False,
),
)
# 2) Walk the typed Images array.
for img in response.bounding_boxes.images or []:
print(img.id, img.visual_type, img.chart_title, img.image_url)
# 3) Fetch the bytes for the first chart.
import re
img = response.bounding_boxes.images[0]
m = re.search(r"/results/([^/]+)/images/([^/?#]+)", img.image_url)
job_id, filename = m.group(1), m.group(2)
# `get_image` returns an iterator of byte chunks — join to get the full PNG.
chunks = list(client.results.get_image(job_id=job_id, filename=filename))
png_bytes = b"".join(chunks)
with open("chart.png", "wb") as f:
f.write(png_bytes)
import { PulseClient } from "pulse-ts-sdk";
import * as fs from "node:fs";
const client = new PulseClient({ apiKey: "YOUR_API_KEY" });
// 1) Extract a workbook and ask for image URLs.
const response = await client.extract({
file: fs.createReadStream("financials.xlsx"),
figureProcessing: { showImages: true },
});
// 2) Walk the typed Images array.
for (const img of response.boundingBoxes?.Images ?? []) {
console.log(img.id, img.visualType, img.chartTitle, img.imageUrl);
}
// 3) Fetch the bytes for the first chart.
const url = response.boundingBoxes?.Images?.[0]?.imageUrl;
const m = url?.match(/\/results\/([^/]+)\/images\/([^/?#]+)/);
const [, jobId, filename] = m!;
const image = await client.results.getImage({ jobId, filename });
// image is a binary response — consume per your runtime (e.g. `await image.bytes()`).
# Step 1: extract and capture the image_url.
curl -sS -X POST https://api.runpulse.com/extract \
-H "x-api-key: $PULSE_API_KEY" \
-F "file=@financials.xlsx" \
-F 'figure_processing={"show_images": true}' \
| jq -r '.bounding_boxes.Images[0].image_url'
# -> https://api.runpulse.com/results/13e3e75f-.../images/excel_image_1_1.png
# Step 2: fetch the PNG bytes.
curl -sS -X GET "https://api.runpulse.com/results/13e3e75f-.../images/excel_image_1_1.png" \
-H "x-api-key: $PULSE_API_KEY" \
-o chart.png
Authentication
Every request must present a validx-api-key header for the org that owns the extraction. Visual artifacts are independently addressable resources; there is no anonymous fallback or public link.
- Authenticated same-org calls (your
x-api-keymatches the org that produced the extraction): succeed for as long as the underlying artifact is retained — same window as any other extraction artifact for that org. - Missing credentials (no
x-api-keyheader): rejected with401 Unauthorized(AUTH_001). - Cross-org authenticated calls (valid key, but not the owning org): rejected with
403 Forbidden(AUTH_002).
Pulse(api_key=...) / new PulseClient({ apiKey: ... }) constructor will attach x-api-key to every results.getImage fetch automatically.
image_url directly in public UIs (e.g. a server-rendered HTML page exposed to unauthenticated visitors) will fail with 401. For public/anonymous embeds, fetch the bytes server-side using your API key and re-host them, or proxy them through your own auth layer.image_url are fine — the link is multi-use. Fetching does not consume the parent extraction’s result-delivery slot, so one extraction can produce many image URLs and each can be downloaded as many times as needed.Errors
| Status | Code | Meaning |
|---|---|---|
400 Bad Request | INVALID_REQUEST | The filename path segment failed safe-filename validation. |
401 Unauthorized | AUTH_001 | No x-api-key (or no valid signed-in session) was supplied. |
403 Forbidden | AUTH_002 | The caller is authenticated but does not belong to the org that owns this extraction. |
404 Not Found | NOT_FOUND | Job or visual image not found. The jobId or filename is wrong, or the artifact has been garbage-collected. |
Next Steps
Bounding Boxes
Images, Tables, Text, Title, and Footer arrays.Extract Endpoint
figure_processing.show_images to populate image_url.Authorizations
Path Parameters
Job identifier — same value used in the image_url returned from /extract.
Visual filename — e.g. excel_image_1_1.png. Must be the exact filename segment from the image_url.
Response
Visual image bytes (image/png or image/jpeg).
The response is of type file.