Streaming

wss wss://ws.xsignalfeed.com/v1/stream

Verbinden

De stream neemt uw sleutel op dezelfde drie manieren aan als REST. Gebruik vanaf een server een header; gebruik de URL-parameter alleen waar geen headers te zetten zijn. Komt de sleutel niet aan of wordt hij niet geaccepteerd, dan gaat de verbinding open en direct weer dicht met 4001, en de sluitreden zegt waarom.

Zo stuurt u de sleutelGebruik het als
Authorization: Bearer xsf_…Aanbevolen voor servers; dezelfde header als bij REST
X-API-Key: xsf_…Gelijkwaardig aan de vorige
?api_key=xsf_…Browsers, en clients die bij de handshake geen headers kunnen zetten. De sleutel wordt deel van de URL, dus houd zulke URL's uit logs
python — sleutel in een header
import websockets

async with websockets.connect("wss://ws.xsignalfeed.com/v1/stream",
                              additional_headers={"Authorization": "Bearer xsf_your_key"}) as ws:
    print(await ws.recv())    # {"t": "system.hello", ...}
QueryparameterBetekenis
api_keyUw sleutel, wanneer u de header niet kunt zetten
last_idHervat na deze sid. Wordt stilzwijgend genegeerd tenzij de sleutel de replay-scope heeft
import asyncio, json, websockets

async def main():
    url = "wss://ws.xsignalfeed.com/v1/stream?api_key=xsf_your_key"
    async with websockets.connect(url) as ws:
        async for raw in ws:
            ev = json.loads(raw)
            if ev["t"].startswith("system."): continue
            print(ev["t"], ev["target"]["screen_name"], ev["d"].get("text", "")[:60])

asyncio.run(main())

Frame

Drie frames komen van ons in plaats van van X:

FrameWanneer
system.helloEerste frame. targets is het aantal accounts dat deze sleutel op dit moment volgt (later toegevoegde accounts doen mee zonder opnieuw te verbinden); replay_from geeft de last_id terug waarmee je opnieuw verbond en is null als je er geen stuurde, en dan wordt er niets opnieuw afgespeeld
system.pingVerzonden tijdens stilte als keepalive, zodat inactieve proxy's de socket niet sluiten
system.errorEr is iets mis met de verbinding; een sluiting volgt

Sluitcode

De handshake slaagt altijd en de sluiting volgt onmiddellijk, zodat uw clientbibliotheek de code en de reden ziet in plaats van een kale HTTP-fout.

SluitcodeBetekenis
1012Herstart van de dienst tijdens onderhoud. Verbind opnieuw met backoff en geef de laatste verwerkte sid mee als ?last_id=: de events van de onderbreking worden opnieuw afgespeeld, er gaat niets verloren
4001De sleutel werd niet geaccepteerd. De sluitreden zegt waarom: missing_api_key (er kwam geen sleutel bij ons aan: controleer de headernaam, of dat uw proxy of clientbibliotheek hem doorgeeft), invalid_api_key (zo'n sleutel bestaat niet: een typfout, of een geheim dat door een rotatie is vervangen), key_revoked (de sleutel is ingetrokken)
4002Te veel gelijktijdige verbindingen voor deze sleutel; een system.error-frame gaat aan de sluiting vooraf
4003De sleutel mist de stream-scope

Correct opnieuw verbinden

Schrijf dit één keer, en niet de naïeve versie. De twee dingen die ertoe doen zijn exponentiële backoff en het bewaren van sid op een plek die een procesherstart overleeft.

python — productievorm
import asyncio, json, pathlib, random, websockets

STATE = pathlib.Path("xsf.cursor")          # survives restarts; a DB row is better still

async def run(key, handle):
    backoff = 1
    while True:
        last = STATE.read_text().strip() if STATE.exists() else None
        url = f"wss://ws.xsignalfeed.com/v1/stream?api_key={key}" + (f"&last_id={last}" if last else "")
        try:
            async with websockets.connect(url, ping_interval=20, max_size=2**22) as ws:
                backoff = 1
                async for raw in ws:
                    ev = json.loads(raw)
                    if ev["t"].startswith("system."): continue
                    handle(ev)                      # must be idempotent: dedupe on ev["id"]
                    if ev.get("sid"): STATE.write_text(ev["sid"])   # only after handling
        except Exception as e:
            print("disconnected:", e)
        await asyncio.sleep(backoff + random.random())   # jitter, or every client returns at once
        backoff = min(60, backoff * 2)