[{"data":1,"prerenderedAt":285},["ShallowReactive",2],{"doc-\u002Fapi\u002Fstreaming":3},{"id":4,"title":5,"body":6,"description":275,"edit":276,"extension":277,"meta":278,"navigation":279,"path":280,"seo":281,"stem":282,"vertical":276,"weight":283,"__hash__":284},"content\u002Fapi\u002Fstreaming.md","Streaming measurements",{"type":7,"value":8,"toc":265},"minimark",[9,13,18,32,39,43,54,57,68,71,75,87,90,103,107,131,135,138,235,238,242,245],[10,11,12],"p",{},"The OpenSense API is mostly request\u002Fresponse. For real-time\ndownstream consumers, there are three options, in order of\npreference.",[14,15,17],"h2",{"id":16},"option-a-webhooks-recommended","Option A — webhooks (recommended)",[10,19,20,21,26,27,31],{},"Configure a ",[22,23,25],"a",{"href":24},"\u002Fintegrations\u002Fwebhooks","webhook integration"," for the\nevent kinds you care about. We push to your URL within ~1 s of the\nevent. This is the right answer for ",[28,29,30],"strong",{},"alarms, ack, clear, device\nstate",".",[10,33,34,35,38],{},"It is ",[28,36,37],{},"not"," the right answer for \"raw measurements\". We do not\nfire a webhook per measurement; the rate would be impractical and\nthe use case rare.",[14,40,42],{"id":41},"option-b-mqtt-subscribe-team-tier","Option B — MQTT subscribe (Team tier)",[10,44,45,46,49,50,53],{},"The MQTT bridge that ingests ",[28,47,48],{},"from"," customer brokers can also be\nconfigured to publish ",[28,51,52],{},"to"," customer brokers — measurements,\nevents, and notifications mirrored in real time.",[10,55,56],{},"Today this is a Team-tier feature; the topic structure is:",[58,59,64],"pre",{"className":60,"code":62,"language":63},[61],"language-text","opensense\u002F\u003Cacc>\u002F\u003Cdevice>\u002Fmeasurement      QoS 1, retained: false\nopensense\u002F\u003Cacc>\u002F\u003Cdevice>\u002Fevent             QoS 1, retained: false\nopensense\u002F\u003Cacc>\u002F\u003Cdevice>\u002Fstate             QoS 1, retained: true\n","text",[65,66,62],"code",{"__ignoreMap":67},"",[10,69,70],{},"Authentication is the same mTLS as inbound. Direction is configured\nper topic family in the dashboard.",[14,72,74],{"id":73},"option-c-polling","Option C — polling",[10,76,77,78,82,83,86],{},"For small-scale integrations, a 60-s poll of the\n",[22,79,81],{"href":80},"\u002Fapi\u002Fmeasurements","measurements API"," with ",[65,84,85],{},"agg=raw&limit=10"," is\nfine and explicitly supported. The 120-req\u002Fmin\u002Faccount rate budget\nis generous enough that ~50 channels polling each 60 s is\ncomfortable.",[10,88,89],{},"Polling is the right answer for:",[91,92,93,97,100],"ul",{},[94,95,96],"li",{},"Home Assistant dashboards.",[94,98,99],{},"Bespoke Grafana panels (where you have not yet plugged the JSON\ndatasource into our API).",[94,101,102],{},"A 12-hour cron that grabs yesterday's data for a separate archive.",[14,104,106],{"id":105},"what-we-do-not-offer","What we do not offer",[91,108,109,119,125],{},[94,110,111,114,115,118],{},[28,112,113],{},"Server-Sent Events"," or ",[28,116,117],{},"WebSockets",". We considered both; the\nwebhook + MQTT pair covers everything they would address. Adding\na third stream is overhead for no new capability.",[94,120,121,124],{},[28,122,123],{},"GraphQL subscriptions",". We are not a GraphQL API.",[94,126,127,130],{},[28,128,129],{},"Long-polling",". The 5-second budget on HTTP handlers makes\nlong-polling awkward and we do not want to enlarge it.",[14,132,134],{"id":133},"latency-budget","Latency budget",[10,136,137],{},"Per layer of the pipeline:",[139,140,141,157],"table",{},[142,143,144],"thead",{},[145,146,147,151,154],"tr",{},[148,149,150],"th",{},"Step",[148,152,153],{},"p50",[148,155,156],{},"p99",[158,159,160,172,182,193,204,215,225],"tbody",{},[145,161,162,166,169],{},[163,164,165],"td",{},"Edge accept → ingest service",[163,167,168],{},"2 ms",[163,170,171],{},"25 ms",[145,173,174,177,179],{},[163,175,176],{},"Ingest validation + DB write",[163,178,168],{},[163,180,181],{},"50 ms",[145,183,184,187,190],{},[163,185,186],{},"Rule engine evaluation",[163,188,189],{},"1 ms",[163,191,192],{},"30 ms",[145,194,195,198,201],{},[163,196,197],{},"Outbound dispatcher → Telegram",[163,199,200],{},"200 ms",[163,202,203],{},"2 s",[145,205,206,209,212],{},[163,207,208],{},"Outbound dispatcher → Email (Postmark)",[163,210,211],{},"1 s",[163,213,214],{},"8 s",[145,216,217,220,222],{},[163,218,219],{},"Outbound dispatcher → Webhook",[163,221,181],{},[163,223,224],{},"500 ms",[145,226,227,230,233],{},[163,228,229],{},"Outbound dispatcher → MQTT publish",[163,231,232],{},"5 ms",[163,234,192],{},[10,236,237],{},"End-to-end alarm-to-Telegram p99: ~3 s from the moment the device\nsends the breach to the moment the message appears in the chat.",[14,239,241],{"id":240},"backpressure","Backpressure",[10,243,244],{},"If your subscribing system is slow:",[91,246,247,253,259],{},[94,248,249,252],{},[28,250,251],{},"MQTT subscribe",": if your broker's queue grows, we throttle.\nOur bridge will drop oldest queued messages above 10 000 per\ntopic — you receive a small gap, not unbounded growth.",[94,254,255,258],{},[28,256,257],{},"Webhook",": we retry with backoff; sustained failure removes the\ndestination from the active set and we email you.",[94,260,261,264],{},[28,262,263],{},"Polling",": it is your concern; we serve as fast as you ask, up\nto the rate-limit budget.",{"title":67,"searchDepth":266,"depth":266,"links":267},3,[268,270,271,272,273,274],{"id":16,"depth":269,"text":17},2,{"id":41,"depth":269,"text":42},{"id":73,"depth":269,"text":74},{"id":105,"depth":269,"text":106},{"id":133,"depth":269,"text":134},{"id":240,"depth":269,"text":241},"How to read in real time",null,"md",{},true,"\u002Fapi\u002Fstreaming",{"title":5,"description":275},"api\u002Fstreaming",299,"_4d-_eJaKssOZovU56eLyPkKr3VrJBpP6YXigWNiids",1779022954737]