# GFPGAN > Restore blurry, old or AI-generated faces in a photo and get back a sharper image at twice the size or more, with the whole frame enhanced rather than just the face. ## Overview - **Endpoint**: `https://queue.modelrunner.run/tencentarc/gfpgan` - **Model ID**: `tencentarc/gfpgan` - **Category**: image-to-image - **Kind**: inference - **Tags**: face-restoration, gfpgan, photo-restoration, old-photo, restore-old-photos, face-enhancement, photo-repair, blind-face-restoration, portrait-enhancement, ai-generated-faces, upscale, image-upscaler, image-to-image ## Pricing - **Estimated Price**: $0.0017385 average per output ## Request Lifecycle This model runs on the ModelRunner **asynchronous queue API** — a single POST does not return the output. Every call requires an `Authorization: Key $MODEL_RUNNER_KEY` header. Run three steps: 1. **Submit** — `POST https://queue.modelrunner.run/tencentarc/gfpgan` with a JSON body holding the input fields at the top level. The body may also include a reserved top-level `metadata` object — a flat string map (max 16 keys, key ≤64 / value ≤512 chars) stored on the request for your own tagging. It is never sent to the model; filter your request history with `GET https://queue.modelrunner.run/requests?metadata=` (exact key=value matches, AND-ed). The response carries request handles only (no output yet): ```json { "status": "IN_QUEUE", "request_id": "<21-char id>", "status_url": "https://queue.modelrunner.run/tencentarc/gfpgan/requests//status", "response_url": "https://queue.modelrunner.run/tencentarc/gfpgan/requests/", "cancel_url": "https://queue.modelrunner.run/tencentarc/gfpgan/requests//cancel" } ``` 2. **Poll status** — `GET ` until `status` is `COMPLETED`. Possible values are `IN_QUEUE`, `IN_PROGRESS`, `COMPLETED`, `FAILED`, `CANCELLED`. A `FAILED` request responds with HTTP 400 and an `error` field. 3. **Read result** — `GET `. Returns the finished request, including the generated `output`: ```json { "id": "", "status": "COMPLETED", "output": ..., "input": ... } ``` The JavaScript and Python SDKs below perform steps 2–3 for you. In any language without an SDK (Swift, Go, Kotlin, etc.) you must implement the polling loop and the final result fetch yourself — see the cURL example for the full flow. ### Input Schema - **`image`** (`string`, _required_): URL of the photo to restore. It works on genuinely degraded faces - blurry, low-resolution, heavily compressed, scanned, faded, or a face an image generator rendered badly - and needs no prompt or description of the damage. Every face detected in the frame is restored and the rest of the picture is enhanced at the same time; there is no way to restrict restoration to one subject. An image with no detectable face still succeeds and is still billed: it comes back as a general upscale rather than an error. - **`scale`** (`number`, _optional_): Post-restoration resize - and it does NOT behave like a plain output multiplier. Restoration always runs at a fixed 2x, and this value then resizes that result off the dimensions of the image that WENT INTO restoration - normally the file you uploaded - so measured against the file you sent: 1 returns 0.50x, 2 returns 2.00x, 3 returns 1.50x, 4 returns 2.00x. The default 2 is therefore both the largest result available and, for an image 300 px tall or taller, the only value that behaves as its name suggests; every other value is a plain interpolation of the already-restored image and adds no detail. One rule compounds with this: an image under 300 px tall is enlarged 2x before restoration begins, so the resize measures off that enlarged copy and each figure above doubles for such an input (1 -> 1.00x, 2 -> 4.00x, 3 -> 3.00x, 4 -> 4.00x). File size grows with the square of the resulting dimensions and the output format is not selectable. - Default: `2` - Range: `1` to `4` - **`version`** (`VersionEnum`, _optional_): Which restoration checkpoint runs. v1.4 (the default) produces more detail and better identity than v1.3; v1.3 gives more natural results and copes better with very low-quality input but is less sharp; v1.2 is sharper and adds a beauty-makeup effect but can look unnatural. - Default: `"v1.4"` - Options: `"v1.2"`, `"v1.3"`, `"v1.4"` ### Output Schema _No `Output` schema properties are available._ ## Default Example **Input** ```json { "image": "https://media.modelrunner.ai/rBY4CCSIeEMxJi5LFGxFn.jpeg", "scale": 2, "version": "v1.4" } ``` **Output** ```json "https://media.modelrunner.ai/T9AdRmzL2XPqZdtGQ1xqU.png" ``` ## Usage Examples ### cURL The queue API is asynchronous: submit the request, poll `status_url` until it is `COMPLETED`, then read the result from `response_url`. Requires `jq`. ```bash # 1. Submit the request (returns request handles, not the output) SUBMIT=$(curl --silent --request POST \ --url https://queue.modelrunner.run/tencentarc/gfpgan \ --header "Authorization: Key $MODEL_RUNNER_KEY" \ --header "Content-Type: application/json" \ --data '{ "image": "https://media.modelrunner.ai/rBY4CCSIeEMxJi5LFGxFn.jpeg", "scale": 2, "version": "v1.4" }') STATUS_URL=$(echo "$SUBMIT" | jq -r '.status_url') RESPONSE_URL=$(echo "$SUBMIT" | jq -r '.response_url') # 2. Poll until the request leaves the queue / in-progress state while true; do STATUS=$(curl --silent --url "$STATUS_URL" \ --header "Authorization: Key $MODEL_RUNNER_KEY" | jq -r '.status') echo "Status: $STATUS" case "$STATUS" in COMPLETED) break ;; FAILED|CANCELLED) echo "Request $STATUS"; exit 1 ;; esac sleep 1 done # 3. Read the finished request, including the generated output curl --silent --url "$RESPONSE_URL" \ --header "Authorization: Key $MODEL_RUNNER_KEY" ``` ### JavaScript ```javascript import { modelrunner } from "@modelrunner/client"; const result = await modelrunner.subscribe("tencentarc/gfpgan", { input: { "image": "https://media.modelrunner.ai/rBY4CCSIeEMxJi5LFGxFn.jpeg", "scale": 2, "version": "v1.4" } }); console.log(result.data); ``` ### Python ```python import asyncio import modelrunner_ai async def main(): response = await modelrunner_ai.submit_async( "tencentarc/gfpgan", arguments={ "image": "https://media.modelrunner.ai/rBY4CCSIeEMxJi5LFGxFn.jpeg", "scale": 2, "version": "v1.4" } ) result = await response.get() print(result["output"]) asyncio.run(main()) ``` ## Additional Resources - [Playground](https://modelrunner.ai/models/tencentarc/gfpgan) - [OpenAPI Schema](https://modelrunner.ai/models/tencentarc/gfpgan/openapi.json) - [LLM Instructions](https://modelrunner.ai/models/tencentarc/gfpgan/llms.txt) - [GitHub](https://github.com/TencentARC/GFPGAN) - [License](https://github.com/TencentARC/GFPGAN/blob/master/LICENSE) - [Paper](https://arxiv.org/abs/2101.04061)