developers · v0.1
Send a frame.
Get back a face.
REST over HTTPS. JSON in, JSON out. The same schema whether you POST a JPEG, a base64 blob, or stream a WebSocket. Sub-20-millisecond on CPU, scales linearly per core, zero per-request setup.
01 / auth
A token. A header.
During the private beta, requests carry an Authorization: Bearer …
header. The token is scoped to your project and rate-limited
per-second, not per-month — Eyeconic is built for live avatars,
not batch jobs.
⏵ beta · request access from the manifesto page
http · headers
POST /v1/analyze HTTP/1.1 Host: api.eyeconic.tech Authorization: Bearer eyc_live_•••••••••••••• Content-Type: multipart/form-data; boundary=… Accept: application/json
02 / endpoints
Three routes. One schema.
POST
/v1/analyze
Multipart upload. One image, one face, full analysis JSON. The path of least resistance.
POST
/v1/analyze/base64
Same response, but the image is sent as a base64 string in JSON. Better for browser → API loops.
GET
/health
Returns
{"ok": true} plus model version. Useful for load balancers and uptime monitors.
03 / code
Wire it up in four lines.
shell · bash
# single-shot analysis from a JPEG on disk curl -X POST https://api.eyeconic.tech/v1/analyze \ -H "Authorization: Bearer $EYC_TOKEN" \ -F "image=@face.jpg"
python · requests
import requests resp = requests.post( "https://api.eyeconic.tech/v1/analyze", headers={"Authorization": f"Bearer {TOKEN}"}, files={"image": open("face.jpg", "rb")}, timeout=2, ) data = resp.json() print(data["eyes"]["left"]["gaze"]) # -> [-0.18, 0.04]
javascript · browser
const grab = async () => { const v = document.querySelector('video'); const c = document.createElement('canvas'); c.width = v.videoWidth; c.height = v.videoHeight; c.getContext('2d').drawImage(v, 0, 0); const r = await fetch('/v1/analyze/base64', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ image_base64: c.toDataURL('image/jpeg', 0.8), }), }); return r.json(); };
unity · C# coroutine
IEnumerator StreamFrame(byte[] jpeg) { var form = new WWWForm(); form.AddBinaryData("image", jpeg, "f.jpg", "image/jpeg"); using var req = UnityWebRequest.Post(EYC_URL, form); req.SetRequestHeader("Authorization", $"Bearer {token}"); yield return req.SendWebRequest(); var face = JsonUtility.FromJson<EyeFrame>(req.downloadHandler.text); ApplyToMetaHuman(face.blendshapes); }
websocket · streaming
const ws = new WebSocket('wss://api.eyeconic.tech/v1/stream'); ws.addEventListener('open', () => { ws.send(JSON.stringify({ token: EYC_TOKEN, fps: 30 })); setInterval(() => ws.send(grabFrame()), 33); }); ws.addEventListener('message', (e) => { const face = JSON.parse(e.data); rig.apply(face.blendshapes); }); // rolling p50: 14.8 ms · client-perceived
04 / blendshape catalog
Fifty-two shapes.
ARKit-compatible names. If you already rig MetaHuman, VRoid, VRM, or Apple's reference faces, the keys map 1:1. No renaming.
▸ eyeBlinkLeft
▸ eyeBlinkRight
▸ eyeLookDownLeft
▸ eyeLookDownRight
▸ eyeLookInLeft
▸ eyeLookInRight
▸ eyeLookOutLeft
▸ eyeLookOutRight
▸ eyeLookUpLeft
▸ eyeLookUpRight
▸ eyeSquintLeft
▸ eyeSquintRight
▸ eyeWideLeft
▸ eyeWideRight
▸ browDownLeft
▸ browDownRight
▸ browInnerUp
▸ browOuterUpLeft
▸ browOuterUpRight
▸ cheekPuff
▸ cheekSquintLeft
▸ cheekSquintRight
▸ noseSneerLeft
▸ noseSneerRight
▸ jawForward
▸ jawLeft
▸ jawRight
▸ jawOpen
▸ mouthClose
▸ mouthFunnel
▸ mouthPucker
▸ mouthLeft
▸ mouthRight
▸ mouthSmileLeft
▸ mouthSmileRight
▸ mouthFrownLeft
▸ mouthFrownRight
▸ mouthDimpleLeft
▸ mouthDimpleRight
▸ mouthStretchLeft
▸ mouthStretchRight
▸ mouthRollLower
▸ mouthRollUpper
▸ mouthShrugLower
▸ mouthShrugUpper
▸ mouthPressLeft
▸ mouthPressRight
▸ mouthLowerDownLeft
▸ mouthLowerDownRight
▸ mouthUpperUpLeft
▸ mouthUpperUpRight
▸ tongueOut
Got a token?
Press a button, you'll be looking at JSON in under two minutes.