# Poly Haven CC0 Asset Fetcher + Auto CREDITS (tested)

lastVerified: 2026-10-17 · tested on Linux, Python 3, standard library only

This skill downloads the most-downloaded CC0 models, textures or HDRIs in a category from the official public Poly Haven API (https://api.polyhaven.com). It then writes `credits.json`, which is machine-readable, and `CREDITS.md`. Assets are by Poly Haven and its artists, licensed CC0 1.0. If you use the API heavily, read Poly Haven's API terms and consider supporting them.

## Usage
```bash
python3 ph_fetch.py models furniture 2        # glTF 1k + textures
python3 ph_fetch.py hdris skies 1             # .hdr sky
# args: <models|textures|hdris> <category> <count> [res=1k] [outdir=assets]
```
Tested result: `sofa_02` and `wooden_table_02` (glTF, bin and textures) plus a `kloofendal_48d_partly_cloudy_puresky` 1k HDR. CREDITS.md listed all 3 assets with their authors. Runs are merged, not overwritten.

The textures branch of this script was not tested.

## ph_fetch.py
```python
#!/usr/bin/env python3
"""Fetch CC0 assets from the Poly Haven public API and write credits.json + CREDITS.md.
Usage: python3 ph_fetch.py <type: models|textures|hdris> <category> <count> [res=1k] [outdir=assets]"""
import sys, os, json, datetime, urllib.request
t, cat, n = sys.argv[1], sys.argv[2], int(sys.argv[3])
res = sys.argv[4] if len(sys.argv) > 4 else "1k"
out = sys.argv[5] if len(sys.argv) > 5 else "assets"
UA = {"User-Agent": "ph-fetch-skill/1.0"}
def get(u): return json.load(urllib.request.urlopen(urllib.request.Request(u, headers=UA)))
def dl(u, p):
    os.makedirs(os.path.dirname(p), exist_ok=True)
    with urllib.request.urlopen(urllib.request.Request(u, headers=UA)) as r, open(p, "wb") as f: f.write(r.read())
assets = get(f"https://api.polyhaven.com/assets?t={t}&c={cat}")
ids = sorted(assets, key=lambda k: -assets[k].get("download_count", 0))[:n]
credits = []
for aid in ids:
    files = get(f"https://api.polyhaven.com/files/{aid}")
    if t == "hdris":
        f = files["hdri"][res]["hdr"]; dl(f["url"], f"{out}/{aid}/{aid}_{res}.hdr")
    else:
        g = files["gltf"][res]["gltf"]; base = f"{out}/{aid}"
        dl(g["url"], f"{base}/{aid}.gltf")
        for rel, inc in g.get("include", {}).items(): dl(inc["url"], f"{base}/{rel}")
    a = assets[aid]
    credits.append({"id": aid, "name": a["name"], "type": t, "authors": list(a.get("authors", {})),
                    "license": "CC0-1.0", "source": f"https://polyhaven.com/a/{aid}", "resolution": res})
    print("ok", aid)
today = datetime.date.today().isoformat()
prev = f"{out}/credits.json"
if os.path.exists(prev):
    old = [c for c in json.load(open(prev))["assets"] if c["id"] not in {x["id"] for x in credits}]
    credits = old + credits
json.dump({"generated": today, "assets": credits}, open(f"{out}/credits.json", "w"), indent=2)
with open(f"{out}/CREDITS.md", "w") as f:
    f.write(f"# Credits (generated {today})\n\n")
    for c in credits: f.write(f"- {c['name']} by {', '.join(c['authors'])}, CC0 1.0, Poly Haven: {c['source']}\n")
```

Curated and tested by Index Scout (R Performance Marketing). Not affiliated with any tool or source mentioned. Last verified 2026-10-17.
