Lightroom API — Batch Image Processing
The production pattern for applying Lightroom-grade adjustments to images at scale. Apply presets, normalize exposure, auto-tone, color grade — across hundreds or thousands of images consistently. Pair with Photoshop API for compositing or with Firefly for generation; Lightroom is the right tool for tonality and color.
When to Use This Skill
Use this skill when:
- A campaign delivers a set of images that need to look like they were processed together
- Mixed-source images (different cameras, lighting conditions) need normalization
- A specific look or preset must be applied consistently
- Raw files (DNG, CR2, NEF, ARW) need processing before downstream use
- The user mentions Lightroom API, presets, auto-tone, batch color grading, or
image.adobe.io
Do NOT use this skill when:
- The transformation is structural (layers, smart objects, compositing) — use Photoshop API
- The need is to generate new image content — use Firefly
- The pipeline only needs to resize/crop — that's also Lightroom-territory but minimal; this skill is overkill
Mental Model
Lightroom API operations split into three families:
| Family | Operations |
|---|---|
| Auto | auto_tone, auto_straighten — Lightroom decides values |
| Adjustments | Set explicit EditOptions values for exposure, highlights, shadows, contrast, vibrance, saturation, and a WhiteBalance preset |
| Preset | Apply a .xmp Lightroom preset file |
A typical batch pipeline combines all three: auto-tone for baseline normalization, then preset for stylistic look, then fine-tune adjustments for specific shots.
Step 1 — Apply a Preset
Lightroom presets are .xmp files containing the develop settings. They are produced in desktop Lightroom and exported.
curl --silent -X POST 'https://image.adobe.io/lrService/presets' \
-H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
-H "X-API-Key: $FIREFLY_SERVICES_CLIENT_ID" \
-H 'Content-Type: application/json' \
-d '{
"inputs": {
"source": {"href": "'"$SOURCE_IMAGE_URL"'", "storage": "external"},
"presets": [{
"href": "'"$PRESET_XMP_URL"'",
"storage": "external"
}]
},
"outputs": [{
"href": "'"$OUTPUT_URL"'",
"storage": "external",
"type": "image/jpeg",
"quality": 9
}]
}'
Response includes a status URL to poll. Same pattern as Photoshop API. (The overwrite output flag applies only to Adobe-storage outputs — omit it for external pre-signed URLs.)
Step 2 — Apply Auto Adjustments
For "make these look consistent" without a specific preset, auto-tone is the right starting point:
curl --silent -X POST 'https://image.adobe.io/lrService/autoTone' \
-H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
-H "X-API-Key: $FIREFLY_SERVICES_CLIENT_ID" \
-H 'Content-Type: application/json' \
-d '{
"inputs": {"href": "'"$SOURCE_IMAGE_URL"'", "storage": "external"},
"outputs": [{"href": "'"$OUTPUT_URL"'", "storage": "external", "type": "image/jpeg"}]
}'
Auto-tone analyzes the image and applies exposure, highlights, shadows, whites, and blacks. It doesn't touch color/style — preset on top of auto-tone is the standard combination.
Step 3 — Manual Adjustments
For shot-specific tuning:
curl --silent -X POST 'https://image.adobe.io/lrService/edit' \
-H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
-H "X-API-Key: $FIREFLY_SERVICES_CLIENT_ID" \
-H 'Content-Type: application/json' \
-d '{
"inputs": {
"source": {"href": "'"$SOURCE_IMAGE_URL"'", "storage": "external"}
},
"options": {
"Exposure": 0.5,
"Highlights": -30,
"Shadows": 25,
"Whites": 10,
"Blacks": -10,
"Contrast": 10,
"Vibrance": 15,
"Saturation": 5,
"WhiteBalance": "As Shot"
},
"outputs": [{"href": "'"$OUTPUT_URL"'", "storage": "external", "type": "image/jpeg"}]
}'
Edit settings go directly under options (the EditOptions object) — there is no adjustments wrapper. Field names are PascalCase. White balance is set with the WhiteBalance enum, not a Kelvin temperature/tint pair.
EditOptions fields
| Field | Range | Effect |
|---|---|---|
Exposure |
-5.0 to +5.0 | Overall brightness in EV; ±1 stop is typical |
Contrast |
-100 to +100 | Global contrast |
Highlights |
-100 to +100 | Brighter areas; negative recovers blown highlights |
Shadows |
-100 to +100 | Darker areas; positive opens shadows |
Whites |
-100 to +100 | White point |
Blacks |
-100 to +100 | Black point |
Vibrance |
-100 to +100 | Saturation of less-saturated colors only |
Saturation |
-100 to +100 | Saturation of all colors (use sparingly) |
Clarity |
-100 to +100 | Midtone contrast |
Dehaze |
-100 to +100 | Atmospheric haze removal/addition |
Texture |
-100 to +100 | Fine detail emphasis |
VignetteAmount |
-100 to +100 | Post-crop vignette |
Sharpness |
0 to 150 | Sharpening amount |
SharpenRadius |
0.5 to 3.0 | Sharpening radius |
SharpenDetail |
0 to 100 | Sharpening detail |
SharpenEdgeMasking |
0 to 10 | Sharpening edge masking |
NoiseReduction |
0 to 100 | Luminance noise reduction |
ColorNoiseReduction |
0 to 100 | Color noise reduction |
WhiteBalance |
enum (below) | White balance preset |
WhiteBalance is an enum, not a Kelvin temperature/tint pair. Valid values:
As Shot · Auto · Cloudy · Custom · Daylight · Flash · Fluorescent · Shade · Tungsten
Step 4 — Composite Pipeline: Auto-Tone + Preset
The standard pattern for normalizing a mixed-source set:
async function normalizeAndStyle({ sourceUrl, presetUrl, outputUrl }) {
// Stage 1: auto-tone — write to an intermediate URL
const intermediateUrl = await getSignedPutUrl(
`intermediates/${randomUUID()}.jpg`,
7200,
);
await submitAutoTone({ sourceUrl, outputUrl: intermediateUrl });
await pollUntilSucceeded();
// Stage 2: apply preset to the auto-toned intermediate
const intermediateGetUrl = await getSignedGetUrl(
`intermediates/${...}`,
7200,
);
await submitPreset({
sourceUrl: intermediateGetUrl,
presetUrl,
outputUrl,
});
await pollUntilSucceeded();
}
Two stages, two jobs. The intermediate file is a chunk of work; persist briefly, delete after final output is confirmed.
For a fully composed batch:
For each input image:
1. auto_straighten
2. auto_tone (sets exposure, highlights, shadows, whites, blacks)
3. apply_preset (sets color/style)
4. fine_tune_adjustments (per-image overrides if needed)
5. Write to final destination
This produces a set of images that read as consistently processed. The exact recipe is brand-defined; the orchestration shape is universal.
Step 5 — Raw File Processing
Lightroom API ingests raw formats directly:
curl --silent -X POST 'https://image.adobe.io/lrService/autoTone' \
-H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
-H "X-API-Key: $FIREFLY_SERVICES_CLIENT_ID" \
-H 'Content-Type: application/json' \
-d '{
"inputs": {"href": "'"$RAW_FILE_URL"'", "storage": "external"},
"outputs": [
{"href": "'"$OUTPUT_JPEG_URL"'", "storage": "external", "type": "image/jpeg", "quality": 10}
]
}'
autoTone accepts multiple outputs in one job — live-verified 2026-08-10: a single submission with both "type": "image/jpeg" and "type": "image/x-adobe-dng" outputs completed with both renditions succeeded. One job, one set of adjustments, delivery JPEG and DNG master together. (Note the input-shape asymmetry across Lightroom endpoints, also live-verified: autoTone takes a flat inputs: {href, storage} object, while presets wraps it as inputs: {source: {...}, presets: [...]}.)
Production Patterns
Pattern: Daily-shoot normalization
A real-estate photography pipeline: hundreds of raw shots per day from various sites, must look brand-consistent.
SQS queue (one message per shot)
↓
Worker:
1. Get pre-signed read for raw
2. Lightroom: auto_straighten → auto_tone → brand_preset
3. Output as JPEG to delivery bucket
4. Output as DNG to master bucket
5. Write metadata to DynamoDB
Throughput is bounded by Lightroom API quota (typically 10 RPM default). Provisioned higher for production at-scale.
Pattern: Campaign-set normalization
A campaign uses 50 hero shots from a single shoot, but lighting varied throughout the day. To make them read as one campaign:
1. Photographer flags one "reference" shot
2. Reference shot is auto-toned, then human-tuned (or pre-defined preset)
3. Export adjustments from reference as a .xmp preset
4. Apply preset to all 49 other shots via Lightroom API
5. Per-shot fine-tune only on outliers
This produces visual cohesion across a campaign set in minutes rather than hours.
Pattern: Pre-processing for Firefly input
Firefly's reference-image generation works best on clean, well-exposed inputs. Lightroom pre-processing improves Firefly output quality:
Raw shot
↓ Lightroom: auto_tone + auto_straighten
Clean source
↓ Firefly: generate_similar / fill / expand
Skipping the Lightroom step often means Firefly produces output that inherits weird color shifts or exposure issues from the source.
Validate
A Lightroom batch pipeline is production-ready when:
- Preset files are versioned in object storage with content-hashes
- Auto-tone + preset is the default; manual adjustments only on outliers
- Intermediate files have a defined lifecycle (auto-delete after final output succeeds)
- Pipeline is queue-fronted with rate-limit awareness
- Output quality settings match the delivery use case (
quality: 8-10for distribution,quality: 12for master) - Raw → JPEG conversion is end-to-end without losing color space information
Troubleshooting & Edge Cases
- Preset not applied: The
.xmpfile is broken or has wrong format. Open in desktop Lightroom to validate, re-export. - Output looks blown out / clipped: Auto-tone results vary by source quality. For better consistency, use a brand preset instead of auto-tone alone.
- Raw file rejected: Some camera-specific raw formats are unsupported. Check the Lightroom API supported formats list. For unsupported raws, convert to DNG first using Adobe DNG Converter.
- Color shifts between batches: White balance varies by source. Set a fixed
WhiteBalancepreset (e.g.Daylight) per-batch instead of relying onAs Shot/auto. - Adjustments take effect but appear weaker than in desktop Lightroom: API uses Camera Raw values; ranges may not map 1:1 to desktop sliders. Test and calibrate against a known reference.
- Rate-limited at 10 RPM despite single workload: Provisioned default is low. Request an increase from the customer's Adobe account team.
Chaining with Other Skills
firefly-services-storage-refs— Input/output URL managementfirefly-services-auth— Token retrievalphotoshop-api-composition— Compose Lightroom-processed images into PSD templatesfirefly-generate-similar— Generate variations from Lightroom-cleaned sourcesfirefly-services-rate-limits— Lightroom has separate quota from Firefly
References
- Lightroom API Documentation
- Adobe DNG Converter (for unsupported raws)
firefly-services-bootstrap— Lightroom API subscription