After the background is removed, products on a plain background can look like they are floating. A soft drop shadow fixes that. It gives the product weight and makes the image look like a studio shot instead of a cutout. FAPIhub's shadow endpoint removes the background and adds the shadow in one request.
The request
The /v2/rembg/shadow/ endpoint takes three settings besides the image:
shadow_color: RGBA color of the shadow, for example(0, 0, 0, 150). The last value is opacity from 0 to 255.offset: how far the shadow moves in pixels, as(x, y).blur_radius: how soft the shadow edge is.
curl -X POST https://fapihub.com/v2/rembg/shadow/ \
-H "ApiKey: YOUR_API_KEY" \
-F "image=@sneaker.jpg" \
-F "shadow_color=(0, 0, 0, 150)" \
-F "offset=(10, 10)" \
-F "blur_radius=15" \
--output sneaker-shadow.pngimport requests
with open("sneaker.jpg", "rb") as image_file:
response = requests.post(
"https://fapihub.com/v2/rembg/shadow/",
headers={"ApiKey": "YOUR_API_KEY"},
files={"image": image_file},
data={
"shadow_color": "(0, 0, 0, 150)",
"offset": "(10, 10)",
"blur_radius": 15,
},
timeout=60,
)
response.raise_for_status()
with open("sneaker-shadow.png", "wb") as out:
out.write(response.content)
Settings that look natural
| Style | shadow_color | offset | blur_radius |
|---|---|---|---|
| Soft catalog shadow | (0, 0, 0, 90) | (6, 6) | 20 |
| Standard drop shadow | (0, 0, 0, 150) | (10, 10) | 15 |
| Strong, dramatic | (0, 0, 0, 200) | (18, 18) | 8 |
| Tinted (on colored backgrounds) | (40, 30, 90, 110) | (8, 10) | 18 |
Tips for believable shadows
- Lower opacity beats a darker color. Real shadows are rarely solid black. Start around 90 to 150 alpha.
- Softer for bigger images. Scale
blur_radiusandoffsetwith the image size. Values that suit 1,000 px look harsh at 3,000 px. - Match the light direction. If the original photo was lit from the left, a shadow falling to the right looks correct. Keep one direction across the catalog.
- Tint on colored backgrounds. A shadow slightly darker than the background color looks more natural than gray.
Where shadows help most
Shoes, bags, bottles, furniture, and electronics benefit the most because they normally sit on a surface. Flat items like clothing laid flat or printed paper usually look better with a very light shadow or none.
Test settings on your own products in the playground. If you sell on marketplaces with strict main-image rules, check them first. Our Amazon image guide explains what is allowed.
Scale shadow settings with image size
The tips above say to scale offset and blur_radius with the image. In practice that means tuning once at a reference size and deriving the values for everything else, so a 1,000 px thumbnail and a 3,000 px hero image get the same look.
from PIL import Image
REFERENCE_WIDTH = 1000
BASE = {"offset": (10, 10), "blur_radius": 15, "alpha": 150}
def shadow_params(path: str) -> dict:
with Image.open(path) as img:
scale = img.width / REFERENCE_WIDTH
dx, dy = (round(v * scale) for v in BASE["offset"])
return {
"shadow_color": f"(0, 0, 0, {BASE['alpha']})",
"offset": f"({dx}, {dy})",
"blur_radius": max(1, round(BASE["blur_radius"] * scale)),
}
print(shadow_params("sneaker.jpg"))
# {'shadow_color': '(0, 0, 0, 150)', 'offset': '(20, 20)', 'blur_radius': 30} for a 2,000 px image
Note the formats: shadow_color and offset are sent as tuple strings, while blur_radius is an integer. A malformed value returns a 400 invalid_image error.
Give the shadow room
If the product touches the edge of the frame, the shadow has nowhere to go and gets cut off. Add a margin of plain background before uploading. Pad with a color close to the existing background so the subject detection is not confused by a hard border.
from PIL import Image, ImageOps
with Image.open("sneaker.jpg") as img:
margin = round(max(img.size) * 0.08) # 8% on each side
padded = ImageOps.expand(img.convert("RGB"), border=margin, fill=(255, 255, 255))
padded.save("sneaker-padded.jpg", quality=95)
Processing a whole catalog with consistent settings
Shadows only look professional when they are identical across a product grid. Keep the settings in one place, run the catalog through a single script, and never tweak values per image by hand. This version uses a small thread pool and stops immediately if the quota runs out.
import time
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import requests
API_URL = "https://fapihub.com/v2/rembg/shadow/"
HEADERS = {"ApiKey": "YOUR_API_KEY"}
SETTINGS = {"shadow_color": "(0, 0, 0, 90)", "offset": "(6, 6)", "blur_radius": 20}
OUT_DIR = Path("shadowed")
OUT_DIR.mkdir(exist_ok=True)
def process(path: Path) -> str:
target = OUT_DIR / f"{path.stem}.png"
if target.exists():
return f"skip {path.name}"
for attempt in range(4):
with path.open("rb") as image_file:
r = requests.post(API_URL, headers=HEADERS, files={"image": image_file},
data=SETTINGS, timeout=60)
if r.ok:
target.write_bytes(r.content)
return f"ok {path.name}"
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 5)))
elif r.status_code >= 500:
time.sleep(2 ** attempt)
elif r.status_code == 402:
raise SystemExit("Out of credits, stopping the batch")
else:
return f"fail {path.name}: {r.json()['error']['code']}"
return f"fail {path.name}: retries exhausted"
with ThreadPoolExecutor(max_workers=4) as pool:
for line in pool.map(process, sorted(Path("products").glob("*.jpg"))):
print(line)
Skipping files that already have output makes the script safe to re-run after an interruption without paying twice. Our bulk processing guide goes deeper on queues and concurrency.
Placing the result on your catalog background
Check the output before you build around it. If the PNG you get back has an alpha channel, flatten it onto the exact background your store uses so the shadow blends into that color instead of whatever the viewer's page shows. This also lets you save a smaller JPEG for opaque product pages.
from PIL import Image
STORE_BACKGROUND = (255, 255, 255)
with Image.open("sneaker-shadow.png") as img:
if img.mode in ("RGBA", "LA"):
canvas = Image.new("RGB", img.size, STORE_BACKGROUND)
canvas.paste(img, mask=img.getchannel("A"))
img = canvas
img.convert("RGB").save("sneaker-shadow.jpg", quality=92)
The same step in Node.js with Sharp is sharp("sneaker-shadow.png").flatten({ background: "#ffffff" }).jpeg({ quality: 92 }).toFile("sneaker-shadow.jpg").
Common shadow problems
- Double shadows: if the original photo already has a strong cast shadow, it may be partly kept with the subject and then a second shadow is added. Reshoot with flatter lighting, or use a lighter
shadow_coloralpha. - Glass and clear plastic: transparent products have soft, partial edges, so a dark shadow can show through them. Use low opacity and a larger
blur_radius. - Inconsistent direction across a grid: one product with a shadow to the left among others to the right looks like a mistake. Keep one
offsetfor the whole catalog. - Harsh edges: a small
blur_radiuswith high opacity reads as a sticker. Raise the radius before lowering the opacity further.
Cost of a shadow pipeline
The shadow endpoint is on every plan and each call is one request. A store with 1,500 products and four photos per product needs 6,000 requests for the first pass, which fits within the Pro plan's 7,500 monthly requests at $15. Budget a few hundred extra requests for testing settings, since every test call counts. For a one-off catalog cleanup with no ongoing volume, Per Usage at $10 per 1,000 requests ($60 for 6,000) may suit better because those requests never expire. More on fitting image work into a store workflow is in the e-commerce use case.
Frequently asked questions
Does the shadow endpoint also remove the background?
Yes. It detects the product, removes the original background, and adds the drop shadow in the same request, so you do not need to call /v2/rembg/ first.
What does the last number in shadow_color mean?
It is the alpha value, from 0 (invisible) to 255 (fully opaque). Most natural-looking product shadows sit between about 90 and 150.
Can I make the shadow fall upward or to the left?
The offset is an (x, y) pair in pixels. The examples in this guide use positive values, which in standard image coordinates push the shadow right and down and suit a light source at the top left. Preview any other direction in the playground before using it in a batch.
Are shadows allowed on marketplace main images?
Rules differ by marketplace and change over time, so check each one's current image requirements. Many expect a pure white background for the main image, and some are stricter about added effects than others.