Smart Editing uses Imagen’s image-to-image model. No AI Profile is required. The
endpoint surface lives under /v1/i2i/projects/... and parallels the
profile-based flow, but the edit body is much smaller
and the output JPEGs are ready as soon as the project reports Completed. There
is no separate export step.
Shooting real estate?
This page is the endpoint reference. For when to use Smart Editing versus the
profile-based flow, the built-in presets, and genre-specific guidance, see the
Real estate guide.
Flow overview
POST /v1/i2i/projects - create the project.
POST /v1/i2i/projects/{uuid}/get_temporary_upload_links - presigned S3 PUTs.
PUT <upload_link> with an empty Content-Type - upload image bytes.
POST /v1/i2i/projects/{uuid}/edit - run the smart edit.
GET /v1/i2i/projects/{uuid} - poll until status is Completed.
GET /v1/i2i/projects/{uuid}/get_temporary_download_links - download JPEGs.
1. Create the project
POST/v1/i2i/projects
Names must be unique per account. If you omit name or send an empty body, the
server auto-generates one for you.
The request and response shape is the same as the profile-based flow: one file_name per
file, and filenames must be unique. See Uploading for
the full request body, the unique-filenames rule, and multipart upload for large
files.
3. Upload the files
PUT each file to its upload_link with an explicit empty Content-Type header
and no x-api-key. The mechanics are identical to the Quickstart flow and are
documented once on the Uploading page.
4. Trigger Smart Editing
The edit body is much smaller than the profile-based flow: no profile_key, no
photography_type, no export.
using System.Net.Http;using System.Text;using System.Text.Json;using var client = new HttpClient();client.DefaultRequestHeaders.Add("x-api-key", Environment.GetEnvironmentVariable("IMAGEN_API_KEY"));var payload = JsonSerializer.Serialize(new { hdr_merge = true, perspective_correction = true, sky_replacement = true,});var content = new StringContent(payload, Encoding.UTF8, "application/json");var response = await client.PostAsync( $"https://api.imagen-ai.com/v1/i2i/projects/{projectUuid}/edit", content);
{ "data": { "message": "Project ... sent for I2I editing successfully." } }
Fields:
hdr_merge: Merges bracketed exposures before editing.
perspective_correction: Straightens vertical and horizontal lines. Recommended for all interiors.
sky_replacement: Replaces blown-out skies with your account’s default sky template. Use it for exteriors.
callback_url: An optional HTTPS endpoint Imagen will POST to when editing finishes. See Callbacks.
HDR output is downsized in Smart Editing
When hdr_merge=true, brackets are merged and resized before
editing (the long edge is bounded for the I2I model), so the final JPEGs are
not full resolution. If you need full-resolution HDR output, use the
profile-based flow instead.
/edit is idempotent
Calling /edit twice on the same project returns the same success
message and won’t trigger a second edit. It is safe to retry on transient
failures.
5. Poll status
Smart Editing uses a single status. There are no separate edit and export steps.
Fetch the project and read its status. Pass get_thumbnail=false for a faster
polling response. The server skips fetching the thumbnail, so the response returns sooner.
# get_thumbnail=false skips the thumbnail for a faster polling responsecurl 'https://api.imagen-ai.com/v1/i2i/projects/$PROJECT_UUID?get_thumbnail=false' \ --header 'x-api-key: $IMAGEN_API_KEY'
import asyncio, httpx, osasync def wait_for_i2i(project_uuid: str) -> None: async with httpx.AsyncClient() as http: while True: resp = await http.get( f"https://api.imagen-ai.com/v1/i2i/projects/{project_uuid}", params={"get_thumbnail": "false"}, headers={"x-api-key": os.environ["IMAGEN_API_KEY"]}, ) status = resp.json()["data"]["status"] if status == "Completed": return if status == "Failed": raise RuntimeError("Edit failed — contact support") await asyncio.sleep(30)
async function waitForI2I(projectUuid: string): Promise<void> { while (true) { const res = await fetch( `https://api.imagen-ai.com/v1/i2i/projects/${projectUuid}?get_thumbnail=false`, { headers: { 'x-api-key': process.env.IMAGEN_API_KEY! } } ); const { data } = (await res.json()) as { data: { status: string } }; if (data.status === 'Completed') return; if (data.status === 'Failed') throw new Error('Edit failed — contact support'); await new Promise(r => setTimeout(r, 30_000)); }}
import ( "encoding/json" "fmt" "net/http" "os" "time")for { req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.imagen-ai.com/v1/i2i/projects/"+projectUUID+"?get_thumbnail=false", nil) req.Header.Set("x-api-key", os.Getenv("IMAGEN_API_KEY")) resp, _ := http.DefaultClient.Do(req) var result struct { Data struct{ Status string `json:"status"` } `json:"data"` } json.NewDecoder(resp.Body).Decode(&result) resp.Body.Close() switch result.Data.Status { case "Completed": return nil case "Failed": return fmt.Errorf("edit failed — contact support") } time.Sleep(30 * time.Second)}
import java.net.URI;import java.net.http.*;while (true) { var request = HttpRequest.newBuilder() .uri(URI.create("https://api.imagen-ai.com/v1/i2i/projects/" + projectUuid + "?get_thumbnail=false")) .header("x-api-key", System.getenv("IMAGEN_API_KEY")) .GET() .build(); var response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); String status = parseStatus(response.body()); if ("Completed".equals(status)) break; if ("Failed".equals(status)) throw new RuntimeException("Edit failed — contact support"); Thread.sleep(30_000);}
require 'net/http'require 'json'require 'uri'loop do uri = URI("https://api.imagen-ai.com/v1/i2i/projects/#{project_uuid}?get_thumbnail=false") req = Net::HTTP::Get.new(uri) req['x-api-key'] = ENV['IMAGEN_API_KEY'] res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } status = JSON.parse(res.body)['data']['status'] break if status == 'Completed' raise 'Edit failed — contact support' if status == 'Failed' sleep 30end
using System.Net.Http;using System.Text.Json;using var client = new HttpClient();client.DefaultRequestHeaders.Add("x-api-key", Environment.GetEnvironmentVariable("IMAGEN_API_KEY"));while (true) { var response = await client.GetAsync( $"https://api.imagen-ai.com/v1/i2i/projects/{projectUuid}?get_thumbnail=false"); var json = await response.Content.ReadAsStringAsync(); var status = JsonDocument.Parse(json) .RootElement.GetProperty("data").GetProperty("status").GetString(); if (status == "Completed") break; if (status == "Failed") throw new Exception("Edit failed — contact support"); await Task.Delay(TimeSpan.FromSeconds(30));}
The possible statuses are Pending, In Progress, Completed, and Failed. Poll every 30
seconds, or skip polling entirely by passing a callback_url on /edit.
6. Download the edited JPEGs
Once status is Completed, download all output files in one call. Unlike the
profile-based flow, this endpoint does not paginate. Every output file is
returned in a single response.
using System.Net.Http;using var client = new HttpClient();client.DefaultRequestHeaders.Add("x-api-key", Environment.GetEnvironmentVariable("IMAGEN_API_KEY"));var response = await client.GetAsync( $"https://api.imagen-ai.com/v1/i2i/projects/{projectUuid}/get_download_link?file_name={Uri.EscapeDataString("DSC_0001.JPEG")}");var json = await response.Content.ReadAsStringAsync();
Override an output file
To replace an output file after a manual touch-up, request
a presigned PUT URL into the output bucket and PUT the new bytes. This endpoint is available only
after the project reaches Completed.
# 1. Request a presigned PUT URL into the output bucketcurl 'https://api.imagen-ai.com/v1/i2i/projects/$PROJECT_UUID/get_upload_link?file_name=DSC_0001.JPEG' \ --header 'x-api-key: $IMAGEN_API_KEY'# 2. PUT the new bytes to that link with an empty Content-Type (see Uploading)curl -X PUT "<presigned_upload_link>" \ -H 'Content-Type;' \ --upload-file DSC_0001.JPEG
import httpx, osasync with httpx.AsyncClient() as http: link = (await http.get( f"https://api.imagen-ai.com/v1/i2i/projects/{project_uuid}/get_upload_link", params={"file_name": "DSC_0001.JPEG"}, headers={"x-api-key": os.environ["IMAGEN_API_KEY"]}, )).json()["data"] with open("DSC_0001.JPEG", "rb") as f: await http.put(link, content=f.read(), headers={"Content-Type": ""})
import { readFile } from 'fs/promises';const url = new URL( `https://api.imagen-ai.com/v1/i2i/projects/${projectUuid}/get_upload_link`);url.searchParams.set('file_name', 'DSC_0001.JPEG');const { data: link } = await (await fetch(url, { headers: { 'x-api-key': process.env.IMAGEN_API_KEY! },})).json();// Content-Type must be empty for the presigned PUTawait fetch(link, { method: 'PUT', headers: { 'Content-Type': '' }, body: await readFile('DSC_0001.JPEG'),});
using System.Net.Http;using System.Text.Json;using var client = new HttpClient();client.DefaultRequestHeaders.Add("x-api-key", Environment.GetEnvironmentVariable("IMAGEN_API_KEY"));var linkJson = await client.GetStringAsync( $"https://api.imagen-ai.com/v1/i2i/projects/{projectUuid}/get_upload_link?file_name={Uri.EscapeDataString("DSC_0001.JPEG")}");var link = JsonDocument.Parse(linkJson).RootElement.GetProperty("data").GetString();var content = new ByteArrayContent(await File.ReadAllBytesAsync("DSC_0001.JPEG"));content.Headers.ContentType = null; // empty Content-Type for the presigned PUTawait client.PutAsync(link, content);
List your Smart Editing projects
Browse your Smart Editing projects. Results are paginated and support filtering by archive status.