[{"data":1,"prerenderedAt":1485},["ShallowReactive",2],{"blog-post-\u002Fblog\u002Fkafka-streams-in-python":3},{"id":4,"title":5,"body":6,"date":1471,"description":1472,"extension":1473,"meta":1474,"navigation":882,"ogImage":1475,"path":1476,"pinned":1477,"seo":1478,"sitemap":1479,"stem":1480,"tags":1481,"__hash__":1484},"blog\u002Fblog\u002Fkafka-streams-in-python.md","Kafka Streams in Python: Faust vs. Quix Streams",{"type":7,"value":8,"toc":1444},"minimark",[9,20,28,33,120,124,127,134,137,187,190,194,207,306,309,313,320,325,334,339,342,345,352,356,392,397,400,404,407,411,464,468,560,563,567,680,687,691,749,752,756,770,773,777,780,787,791,794,826,833,1243,1246,1258,1262,1269,1319,1328,1332,1338,1346,1367,1375,1379,1383,1396,1400,1409,1413,1416,1420,1423,1427,1430,1437,1440],[10,11,12,19],"p",{},[13,14,18],"a",{"href":15,"rel":16},"https:\u002F\u002Fkafka.apache.org\u002Fdocumentation\u002Fstreams\u002F",[17],"nofollow","Apache Kafka Streams"," processes Kafka records as they arrive. It can transform and join records, calculate results over time, and restore processing state after a restart. Kafka Streams does not provide a Python API. For similar stream-processing tasks in Python, two Kafka-focused options are Faust Streaming and Quix Streams.",[10,21,22,23,27],{},"This guide compares verified capabilities and package versions, then uses GitHub repository metrics as a separate risk check. If your application only needs to produce or consume records, see ",[13,24,26],{"href":25},"\u002Fblog\u002Fkafka-python-clients","Kafka Python Clients 2026: Which Should You Choose?",".",[29,30,32],"h2",{"id":31},"quick-decision","Quick decision",[34,35,36,52],"table",{},[37,38,39],"thead",{},[40,41,42,46,49],"tr",{},[43,44,45],"th",{},"Situation",[43,47,48],{},"Best starting point",[43,50,51],{},"Main caveat",[53,54,55,72,86,97,107],"tbody",{},[40,56,57,61,64],{},[58,59,60],"td",{},"Built-in Schema Registry support or connectors",[58,62,63],{},"Quix Streams",[58,65,66,67,71],{},"Source\u002FSink connectors are beta; ",[68,69,70],"code",{},"StreamingDataFrame"," callbacks are synchronous.",[40,73,74,77,79],{},[58,75,76],{},"Joins or RocksDB-backed state by default",[58,78,63],{},[58,80,81,82,85],{},"Installs the native ",[68,83,84],{},"confluent-kafka"," dependency.",[40,87,88,91,94],{},[58,89,90],{},"asyncio-based processing",[58,92,93],{},"Faust Streaming",[58,95,96],{},"No built-in joins, Schema Registry, or connectors in PyPI 0.13.2.",[40,98,99,102,104],{},[58,100,101],{},"Prototype or local pipeline with in-memory state",[58,103,93],{},[58,105,106],{},"Use RocksDB or another persistent store in production.",[40,108,109,112,117],{},[58,110,111],{},"Application only needs a producer or consumer",[58,113,114],{},[13,115,116],{"href":25},"Compare Python Kafka clients",[58,118,119],{},"A stream-processing framework adds state and recovery operations.",[29,121,123],{"id":122},"when-to-use-a-stream-processing-framework","When to use a stream-processing framework",[10,125,126],{},"Stream processing means handling events continuously as they arrive instead of waiting for a complete batch. A Kafka consumer can handle simple record-by-record work; a stream-processing framework adds state, windows, joins, and recovery.",[10,128,129,130,133],{},"Use a framework when a result depends on earlier events, event time, or another stream—for example rolling metrics, deduplication, or combining two event streams. If every record can be processed independently, a ",[13,131,132],{"href":25},"Kafka Python client"," is usually enough.",[10,135,136],{},"Python stream-processing tools generally fall into three categories:",[34,138,139,152],{},[37,140,141],{},[40,142,143,146,149],{},[43,144,145],{},"Approach",[43,147,148],{},"Examples",[43,150,151],{},"Best fit",[53,153,154,165,176],{},[40,155,156,159,162],{},[58,157,158],{},"Kafka-focused libraries",[58,160,161],{},"Faust Streaming, Quix Streams",[58,163,164],{},"Stateful Kafka applications",[40,166,167,170,173],{},[58,168,169],{},"Python dataflow frameworks",[58,171,172],{},"Bytewax",[58,174,175],{},"Pipelines using Kafka and other sources",[40,177,178,181,184],{},[58,179,180],{},"Distributed engines",[58,182,183],{},"PyFlink, PySpark Structured Streaming",[58,185,186],{},"Large-scale batch and streaming workloads",[10,188,189],{},"This article focuses on Faust Streaming and Quix Streams because their Kafka-focused library model is the closest match to Kafka Streams.",[29,191,193],{"id":192},"faust-streaming-vs-quix-streams","Faust Streaming vs. Quix Streams",[10,195,196,200,201,206],{},[197,198,199],"strong",{},"Capabilities verified against official documentation and package artifacts:"," 13 Aug 2026. For Faust Streaming, the comparison uses the installable PyPI version 0.13.2; ",[13,202,205],{"href":203,"rel":204},"https:\u002F\u002Fgithub.com\u002Ffaust-streaming\u002Ffaust\u002Freleases\u002Ftag\u002Fv0.14.1",[17],"v0.14.1"," is currently available only on GitHub.",[34,208,209,222],{},[37,210,211],{},[40,212,213,216,219],{},[43,214,215],{},"Capability",[43,217,218],{},"Faust Streaming 0.13.2",[43,220,221],{},"Quix Streams 3.25.0",[53,223,224,235,249,262,273,284,295],{},[40,225,226,229,232],{},[58,227,228],{},"Python support",[58,230,231],{},"Python 3.10+",[58,233,234],{},"Python 3.9+",[40,236,237,240,243],{},[58,238,239],{},"Installation",[58,241,242],{},"Platform-specific CPython wheels; optional RocksDB\u002Frocksdict extras",[58,244,245,246,248],{},"Universal Python wheel; native ",[68,247,84],{}," dependency",[40,250,251,254,257],{},[58,252,253],{},"Pipeline API",[58,255,256],{},"asyncio agents",[58,258,259,261],{},[68,260,70],{}," with synchronous callbacks",[40,263,264,267,270],{},[58,265,266],{},"Stateful processing",[58,268,269],{},"Aggregations and windows; no built-in joins",[58,271,272],{},"Aggregations, windows, and joins",[40,274,275,278,281],{},[58,276,277],{},"State and recovery",[58,279,280],{},"Tables + changelog; RocksDB recommended",[58,282,283],{},"RocksDB + changelog by default",[40,285,286,289,292],{},[58,287,288],{},"Schema Registry",[58,290,291],{},"No built-in integration",[58,293,294],{},"Avro, Protobuf, JSON Schema",[40,296,297,300,303],{},[58,298,299],{},"Connectors",[58,301,302],{},"No built-in connectors",[58,304,305],{},"Pre-built and custom Source\u002FSink connectors (beta)",[10,307,308],{},"Both default to at-least-once processing and support Kafka exactly-once processing.",[310,311,93],"h3",{"id":312},"faust-streaming",[10,314,315,319],{},[13,316,93],{"href":317,"rel":318},"https:\u002F\u002Ffaust-streaming.github.io\u002Ffaust\u002Fintroduction.html",[17]," is the active community fork of Robinhood's Faust, an asyncio-based stream-processing library for Kafka. It processes records with async agents and stores state in Tables.",[10,321,322],{},[197,323,324],{},"Capabilities and limitations",[10,326,327,328,333],{},"Tables recover from Kafka changelogs and support time windows. RocksDB is recommended for production state. In 0.13.2, ",[13,329,332],{"href":330,"rel":331},"https:\u002F\u002Fgithub.com\u002Ffaust-streaming\u002Ffaust\u002Fblob\u002Fv0.13.2\u002Ffaust\u002Ftables\u002Fbase.py#L469-L487",[17],"built-in table joins are not implemented",", while Schema Registry support and connectors require custom code. Kafka exactly-once does not cover HTTP or database writes.",[10,335,336],{},[197,337,338],{},"When to choose Faust Streaming",[10,340,341],{},"Choose Faust Streaming when the processor is built around asyncio and needs to await HTTP, database, or other network calls directly.",[310,343,63],{"id":344},"quix-streams",[10,346,347,351],{},[13,348,63],{"href":349,"rel":350},"https:\u002F\u002Fquix.io\u002Fdocs\u002Fquix-streams\u002Fintroduction.html",[17]," is a Python stream-processing library for Kafka. It uses a DataFrame-style API to build processing pipelines.",[10,353,354],{},[197,355,324],{},[10,357,358,359,364,365,370,371,376,377,382,383,385,386,391],{},"Quix Streams includes windows, ",[13,360,363],{"href":361,"rel":362},"https:\u002F\u002Fquix.io\u002Fdocs\u002Fquix-streams\u002Fjoins.html",[17],"joins",", ",[13,366,369],{"href":367,"rel":368},"https:\u002F\u002Fquix.io\u002Fdocs\u002Fquix-streams\u002Fadvanced\u002Fserialization.html",[17],"Schema Registry serializers",", and pre-built ",[13,372,375],{"href":373,"rel":374},"https:\u002F\u002Fquix.io\u002Fdocs\u002Fquix-streams\u002Fconnectors\u002Fsources\u002Findex.html",[17],"Source","\u002F",[13,378,381],{"href":379,"rel":380},"https:\u002F\u002Fquix.io\u002Fdocs\u002Fquix-streams\u002Fconnectors\u002Fsinks\u002Findex.html",[17],"Sink"," connectors, which are currently beta. Its ",[68,384,70],{}," callbacks are synchronous, and ",[13,387,390],{"href":388,"rel":389},"https:\u002F\u002Fquix.io\u002Fdocs\u002Fquix-streams\u002Fadvanced\u002Fcheckpointing.html",[17],"exactly-once processing"," does not cover writes to external sinks.",[10,393,394],{},[197,395,396],{},"When to choose Quix Streams",[10,398,399],{},"Choose Quix Streams for a new stateful Kafka pipeline that needs joins, Schema Registry, or built-in connectors.",[29,401,403],{"id":402},"github-repository-metrics","GitHub repository metrics",[10,405,406],{},"The tables below compare both libraries' GitHub activity, maintenance, and public usage using the same metrics and assessment rules. Snapshot: 13 Aug 2026.",[310,408,410],{"id":409},"project-overview","Project overview",[34,412,413,424],{},[37,414,415],{},[40,416,417,419,422],{},[43,418],{},[43,420,421],{},"Faust",[43,423,63],{},[53,425,426,437,448],{},[40,427,428,431,434],{},[58,429,430],{},"Repository created",[58,432,433],{},"22 Oct 2020",[58,435,436],{},"17 Nov 2022",[40,438,439,442,445],{},[58,440,441],{},"License",[58,443,444],{},"BSD-3-Clause",[58,446,447],{},"Apache-2.0",[40,449,450,453,461],{},[58,451,452],{},"Repository status",[58,454,455,456],{},"Active · ",[13,457,460],{"href":458,"rel":459},"https:\u002F\u002Fgithub.com\u002Ffaust-streaming\u002Ffaust#why-the-fork",[17],"community fork",[58,462,463],{},"Active · original",[310,465,467],{"id":466},"activity","Activity",[34,469,470,481],{},[37,471,472],{},[40,473,474,477,479],{},[43,475,476],{},"Metric",[43,478,421],{},[43,480,63],{},[53,482,483,494,505,516,527,538,549],{},[40,484,485,488,491],{},[58,486,487],{},"Latest default-branch commit",[58,489,490],{},"13 Aug 2026",[58,492,493],{},"11 Aug 2026",[40,495,496,499,502],{},[58,497,498],{},"Latest GitHub Release",[58,500,501],{},"v0.14.1 (11 Aug 2026)",[58,503,504],{},"v3.25.0 (24 Jul 2026)",[40,506,507,510,513],{},[58,508,509],{},"GitHub Releases (12 mo)",[58,511,512],{},"7",[58,514,515],{},"10",[40,517,518,521,524],{},[58,519,520],{},"Commits",[58,522,523],{},"87 (90 d) · 88 (12 mo)",[58,525,526],{},"20 (90 d) · 81 (12 mo)",[40,528,529,532,535],{},[58,530,531],{},"Issue flow (90 d)",[58,533,534],{},"1 opened · 16 closed",[58,536,537],{},"7 opened · 9 closed",[40,539,540,543,546],{},[58,541,542],{},"PR flow (90 d)",[58,544,545],{},"105 opened · 63 merged",[58,547,548],{},"29 opened · 21 merged",[40,550,551,554,557],{},[58,552,553],{},"Activity assessment",[58,555,556],{},"🟢 87 commits and 63 merged PRs in the last 90 days.",[58,558,559],{},"🟢 20 commits and 21 merged PRs in the last 90 days.",[10,561,562],{},"Release figures cover GitHub Releases, not PyPI.",[310,564,566],{"id":565},"maintenance","Maintenance",[34,568,569,579],{},[37,570,571],{},[40,572,573,575,577],{},[43,574,476],{},[43,576,421],{},[43,578,63],{},[53,580,581,592,603,614,625,636,647,658,669],{},[40,582,583,586,589],{},[58,584,585],{},"Active commit authors (12 mo)",[58,587,588],{},"6",[58,590,591],{},"14",[40,593,594,597,600],{},[58,595,596],{},"PR merge distribution (12 mo)",[58,598,599],{},"2 people · Top 1: 98% · Top 2: 100%",[58,601,602],{},"7 people · Top 1: 24% · Top 2: 48%",[40,604,605,608,611],{},[58,606,607],{},"Issue backlog",[58,609,610],{},"118 open · median age 3.6 y",[58,612,613],{},"13 open · median age 1.3 y",[40,615,616,619,622],{},[58,617,618],{},"Issue closure rate",[58,620,621],{},"N\u002FA — empty cohort",[58,623,624],{},"0\u002F3 closed within 30 d · 1\u002F3 within 90 d (small sample)",[40,626,627,630,633],{},[58,628,629],{},"PR backlog",[58,631,632],{},"30 open · median age 21 d",[58,634,635],{},"15 open · median age 275 d",[40,637,638,641,644],{},[58,639,640],{},"Median PR merge time (90 d)",[58,642,643],{},"10.7 h (n=63)",[58,645,646],{},"6.7 d (n=21)",[40,648,649,652,655],{},[58,650,651],{},"Published GitHub security advisories",[58,653,654],{},"1 · 1 without patched version",[58,656,657],{},"0",[40,659,660,663,666],{},[58,661,662],{},"Responsiveness assessment",[58,664,665],{},"🟢 New PRs merge in a median of 10.7 h (n=63); the median open PR age is 21 d.",[58,667,668],{},"🟡 New PRs merge in a median of 6.7 d (n=21), but 15 open PRs have a median age of 275 d.",[40,670,671,674,677],{},[58,672,673],{},"PR merge concentration assessment",[58,675,676],{},"🟡 2 people merged PRs in 12 mo; the most active account handled 98% of merges.",[58,678,679],{},"🟢 7 people merged PRs in 12 mo; the most active account handled 24% of merges.",[10,681,682,683,686],{},"PR merge distribution counts non-bot ",[68,684,685],{},"mergedBy"," accounts, so automated merges may undercount human reviewers. Issue closure rates use issues opened 90–180 days before the snapshot, giving each issue a full 90-day window.",[310,688,690],{"id":689},"public-usage-and-interest","Public usage and interest",[34,692,693,703],{},[37,694,695],{},[40,696,697,699,701],{},[43,698,476],{},[43,700,421],{},[43,702,63],{},[53,704,705,716,727,738],{},[40,706,707,710,713],{},[58,708,709],{},"Stars",[58,711,712],{},"1,882",[58,714,715],{},"1,567",[40,717,718,721,724],{},[58,719,720],{},"Forks",[58,722,723],{},"205",[58,725,726],{},"110",[40,728,729,732,735],{},[58,730,731],{},"GitHub dependents (Used by)",[58,733,734],{},"365",[58,736,737],{},"459",[40,739,740,743,746],{},[58,741,742],{},"Public usage assessment",[58,744,745],{},"🟡 Mixed public usage and interest: 365 dependents, 205 forks, and 1,882 stars.",[58,747,748],{},"🟡 Mixed public usage and interest: 459 dependents, 110 forks, and 1,567 stars.",[10,750,751],{},"GitHub dependents are approximate public-repository counts.",[310,753,755],{"id":754},"overall-repository-signals","Overall repository signals",[757,758,759,765],"ul",{},[760,761,762,764],"li",{},[197,763,93],{}," is active and merges new PRs quickly, but two people handled all recorded merges in the past year and one account handled 98%.",[760,766,767,769],{},[197,768,63],{}," has broader maintainer participation, but its 15 open PRs have a median age of 275 days.",[10,771,772],{},"Both libraries show mixed public usage and interest. Choose by technical fit first, then use these repository signals to assess maintenance risk.",[29,774,776],{"id":775},"test-a-streaming-pipeline-with-kafma","Test a streaming pipeline with Kafma",[10,778,779],{},"A stream-processing test should verify both sides of the pipeline: publish a few test records, then inspect the result written to Kafka.",[10,781,782,783,786],{},"We use ",[13,784,785],{"href":376},"Kafma—the desktop Kafka UI"," to publish three orders and inspect their aggregated result. The example uses Quix Streams to match this guide's starting point for a new stateful pipeline and to demonstrate its built-in count-window API. It sums each customer's orders in groups of three.",[310,788,790],{"id":789},"run-the-pipeline","Run the pipeline",[10,792,793],{},"Install Quix Streams:",[795,796,801],"pre",{"className":797,"code":798,"language":799,"meta":800,"style":800},"language-bash shiki shiki-themes kafma-dark","python -m pip install \"quixstreams==3.25.0\"\n","bash","",[68,802,803],{"__ignoreMap":800},[804,805,808,812,816,820,823],"span",{"class":806,"line":807},"line",1,[804,809,811],{"class":810},"sLaUg","python",[804,813,815],{"class":814},"sUqoa"," -m",[804,817,819],{"class":818},"sJl8Q"," pip",[804,821,822],{"class":818}," install",[804,824,825],{"class":818}," \"quixstreams==3.25.0\"\n",[10,827,828,829,832],{},"Save this pipeline as ",[68,830,831],{},"order_totals.py",":",[795,834,837],{"className":835,"code":836,"language":811,"meta":800,"style":800},"language-python shiki shiki-themes kafma-dark","from quixstreams import Application\nfrom quixstreams.dataframe.windows import Sum\n\napp = Application(\n    broker_address=\"127.0.0.1:9092\",\n    consumer_group=\"kafma-order-totals-v1\",\n    auto_offset_reset=\"latest\",\n)\n\ninput_topic = app.topic(\"order-events\", value_deserializer=\"json\")\noutput_topic = app.topic(\n    \"customer-order-totals\",\n    key_serializer=\"str\",\n    value_serializer=\"json\",\n)\n\nsdf = app.dataframe(input_topic)\nsdf = sdf.group_by(\"customer_id\")\nsdf = (\n    sdf.tumbling_count_window(count=3)\n    .agg(total_amount=Sum(\"amount\"))\n    .final()\n)\nsdf = sdf.print(metadata=True)\nsdf.to_topic(output_topic)\n\nif __name__ == \"__main__\":\n    app.run()\n",[68,838,839,854,877,884,899,913,926,939,945,950,985,1001,1009,1022,1034,1039,1044,1065,1087,1097,1120,1147,1158,1163,1189,1207,1212,1230],{"__ignoreMap":800},[804,840,841,845,848,851],{"class":806,"line":807},[804,842,844],{"class":843},"sifj9","from",[804,846,847],{"class":814}," quixstreams ",[804,849,850],{"class":843},"import",[804,852,853],{"class":814}," Application\n",[804,855,857,859,862,864,867,869,872,874],{"class":806,"line":856},2,[804,858,844],{"class":843},[804,860,861],{"class":814}," quixstreams",[804,863,27],{"class":843},[804,865,866],{"class":814},"dataframe",[804,868,27],{"class":843},[804,870,871],{"class":814},"windows ",[804,873,850],{"class":843},[804,875,876],{"class":814}," Sum\n",[804,878,880],{"class":806,"line":879},3,[804,881,883],{"emptyLinePlaceholder":882},true,"\n",[804,885,887,890,893,896],{"class":806,"line":886},4,[804,888,889],{"class":814},"app ",[804,891,892],{"class":843},"=",[804,894,895],{"class":814}," Application",[804,897,898],{"class":843},"(\n",[804,900,902,905,907,910],{"class":806,"line":901},5,[804,903,904],{"class":814},"    broker_address",[804,906,892],{"class":843},[804,908,909],{"class":818},"\"127.0.0.1:9092\"",[804,911,912],{"class":843},",\n",[804,914,916,919,921,924],{"class":806,"line":915},6,[804,917,918],{"class":814},"    consumer_group",[804,920,892],{"class":843},[804,922,923],{"class":818},"\"kafma-order-totals-v1\"",[804,925,912],{"class":843},[804,927,929,932,934,937],{"class":806,"line":928},7,[804,930,931],{"class":814},"    auto_offset_reset",[804,933,892],{"class":843},[804,935,936],{"class":818},"\"latest\"",[804,938,912],{"class":843},[804,940,942],{"class":806,"line":941},8,[804,943,944],{"class":843},")\n",[804,946,948],{"class":806,"line":947},9,[804,949,883],{"emptyLinePlaceholder":882},[804,951,953,956,958,961,963,966,969,972,975,978,980,983],{"class":806,"line":952},10,[804,954,955],{"class":814},"input_topic ",[804,957,892],{"class":843},[804,959,960],{"class":814}," app",[804,962,27],{"class":843},[804,964,965],{"class":814},"topic",[804,967,968],{"class":843},"(",[804,970,971],{"class":818},"\"order-events\"",[804,973,974],{"class":843},",",[804,976,977],{"class":814}," value_deserializer",[804,979,892],{"class":843},[804,981,982],{"class":818},"\"json\"",[804,984,944],{"class":843},[804,986,988,991,993,995,997,999],{"class":806,"line":987},11,[804,989,990],{"class":814},"output_topic ",[804,992,892],{"class":843},[804,994,960],{"class":814},[804,996,27],{"class":843},[804,998,965],{"class":814},[804,1000,898],{"class":843},[804,1002,1004,1007],{"class":806,"line":1003},12,[804,1005,1006],{"class":818},"    \"customer-order-totals\"",[804,1008,912],{"class":843},[804,1010,1012,1015,1017,1020],{"class":806,"line":1011},13,[804,1013,1014],{"class":814},"    key_serializer",[804,1016,892],{"class":843},[804,1018,1019],{"class":818},"\"str\"",[804,1021,912],{"class":843},[804,1023,1025,1028,1030,1032],{"class":806,"line":1024},14,[804,1026,1027],{"class":814},"    value_serializer",[804,1029,892],{"class":843},[804,1031,982],{"class":818},[804,1033,912],{"class":843},[804,1035,1037],{"class":806,"line":1036},15,[804,1038,944],{"class":843},[804,1040,1042],{"class":806,"line":1041},16,[804,1043,883],{"emptyLinePlaceholder":882},[804,1045,1047,1050,1052,1054,1056,1058,1060,1063],{"class":806,"line":1046},17,[804,1048,1049],{"class":814},"sdf ",[804,1051,892],{"class":843},[804,1053,960],{"class":814},[804,1055,27],{"class":843},[804,1057,866],{"class":814},[804,1059,968],{"class":843},[804,1061,1062],{"class":814},"input_topic",[804,1064,944],{"class":843},[804,1066,1068,1070,1072,1075,1077,1080,1082,1085],{"class":806,"line":1067},18,[804,1069,1049],{"class":814},[804,1071,892],{"class":843},[804,1073,1074],{"class":814}," sdf",[804,1076,27],{"class":843},[804,1078,1079],{"class":814},"group_by",[804,1081,968],{"class":843},[804,1083,1084],{"class":818},"\"customer_id\"",[804,1086,944],{"class":843},[804,1088,1090,1092,1094],{"class":806,"line":1089},19,[804,1091,1049],{"class":814},[804,1093,892],{"class":843},[804,1095,1096],{"class":843}," (\n",[804,1098,1100,1103,1105,1108,1110,1113,1115,1118],{"class":806,"line":1099},20,[804,1101,1102],{"class":814},"    sdf",[804,1104,27],{"class":843},[804,1106,1107],{"class":814},"tumbling_count_window",[804,1109,968],{"class":843},[804,1111,1112],{"class":814},"count",[804,1114,892],{"class":843},[804,1116,1117],{"class":818},"3",[804,1119,944],{"class":843},[804,1121,1123,1126,1129,1131,1134,1136,1139,1141,1144],{"class":806,"line":1122},21,[804,1124,1125],{"class":843},"    .",[804,1127,1128],{"class":814},"agg",[804,1130,968],{"class":843},[804,1132,1133],{"class":814},"total_amount",[804,1135,892],{"class":843},[804,1137,1138],{"class":814},"Sum",[804,1140,968],{"class":843},[804,1142,1143],{"class":818},"\"amount\"",[804,1145,1146],{"class":843},"))\n",[804,1148,1150,1152,1155],{"class":806,"line":1149},22,[804,1151,1125],{"class":843},[804,1153,1154],{"class":814},"final",[804,1156,1157],{"class":843},"()\n",[804,1159,1161],{"class":806,"line":1160},23,[804,1162,944],{"class":843},[804,1164,1166,1168,1170,1172,1174,1177,1179,1182,1184,1187],{"class":806,"line":1165},24,[804,1167,1049],{"class":814},[804,1169,892],{"class":843},[804,1171,1074],{"class":814},[804,1173,27],{"class":843},[804,1175,1176],{"class":814},"print",[804,1178,968],{"class":843},[804,1180,1181],{"class":814},"metadata",[804,1183,892],{"class":843},[804,1185,1186],{"class":818},"True",[804,1188,944],{"class":843},[804,1190,1192,1195,1197,1200,1202,1205],{"class":806,"line":1191},25,[804,1193,1194],{"class":814},"sdf",[804,1196,27],{"class":843},[804,1198,1199],{"class":814},"to_topic",[804,1201,968],{"class":843},[804,1203,1204],{"class":814},"output_topic",[804,1206,944],{"class":843},[804,1208,1210],{"class":806,"line":1209},26,[804,1211,883],{"emptyLinePlaceholder":882},[804,1213,1215,1218,1221,1224,1227],{"class":806,"line":1214},27,[804,1216,1217],{"class":843},"if",[804,1219,1220],{"class":814}," __name__ ",[804,1222,1223],{"class":843},"==",[804,1225,1226],{"class":818}," \"__main__\"",[804,1228,1229],{"class":843},":\n",[804,1231,1233,1236,1238,1241],{"class":806,"line":1232},28,[804,1234,1235],{"class":814},"    app",[804,1237,27],{"class":843},[804,1239,1240],{"class":814},"run",[804,1242,1157],{"class":843},[10,1244,1245],{},"Start the pipeline before publishing the test records:",[795,1247,1249],{"className":797,"code":1248,"language":799,"meta":800,"style":800},"python order_totals.py\n",[68,1250,1251],{"__ignoreMap":800},[804,1252,1253,1255],{"class":806,"line":807},[804,1254,811],{"class":810},[804,1256,1257],{"class":818}," order_totals.py\n",[310,1259,1261],{"id":1260},"publish-records-with-kafma","Publish records with Kafma",[10,1263,1264,1265,1268],{},"In Kafma, publish these three JSON records to ",[68,1266,1267],{},"order-events",". Use the listed Kafka key for each record:",[34,1270,1271,1281],{},[37,1272,1273],{},[40,1274,1275,1278],{},[43,1276,1277],{},"Key",[43,1279,1280],{},"Value",[53,1282,1283,1295,1307],{},[40,1284,1285,1290],{},[58,1286,1287],{},[68,1288,1289],{},"order_1",[58,1291,1292],{},[68,1293,1294],{},"{\"customer_id\":\"c_10\",\"amount\":12}",[40,1296,1297,1302],{},[58,1298,1299],{},[68,1300,1301],{},"order_2",[58,1303,1304],{},[68,1305,1306],{},"{\"customer_id\":\"c_10\",\"amount\":18}",[40,1308,1309,1314],{},[58,1310,1311],{},[68,1312,1313],{},"order_3",[58,1315,1316],{},[68,1317,1318],{},"{\"customer_id\":\"c_10\",\"amount\":6}",[10,1320,1321],{},[1322,1323],"img",{"alt":1324,"height":1325,"src":1326,"width":1327},"Kafma showing three order events for customer c_10",1520,"https:\u002F\u002Fmedia.kafma.app\u002Fblog\u002Fkafka-streams-in-python\u002Fkafma-order-events.png",2160,[310,1329,1331],{"id":1330},"inspect-the-output-with-kafma","Inspect the output with Kafma",[10,1333,1334,1335,27],{},"After you publish the third record, the pipeline prints the total in the terminal and writes it to ",[68,1336,1337],{},"customer-order-totals",[10,1339,1340],{},[1322,1341],{"alt":1342,"height":1343,"src":1344,"width":1345},"Quix Streams terminal output showing a total amount of 36 for customer c_10",414,"https:\u002F\u002Fmedia.kafma.app\u002Fblog\u002Fkafka-streams-in-python\u002Fquix-streams-order-total.png",1994,[10,1347,1348,1349,1351,1352,364,1355,1358,1359,1362,1363,1366],{},"Open ",[68,1350,1337],{}," in Kafma. The result should have key ",[68,1353,1354],{},"c_10",[68,1356,1357],{},"\"total_amount\":36",", and the window's ",[68,1360,1361],{},"start"," and ",[68,1364,1365],{},"end"," timestamps.",[10,1368,1369],{},[1322,1370],{"alt":1371,"height":1372,"src":1373,"width":1374},"Kafma showing the customer-order-totals record with a total amount of 36",822,"https:\u002F\u002Fmedia.kafma.app\u002Fblog\u002Fkafka-streams-in-python\u002Fkafma-customer-order-total.png",1564,[29,1376,1378],{"id":1377},"frequently-asked-questions","Frequently asked questions",[310,1380,1382],{"id":1381},"is-faust-the-same-as-faust-streaming","Is Faust the same as Faust Streaming?",[10,1384,1385,1386,1388,1389,1392,1393,1395],{},"No. This guide compares Faust Streaming, the community-maintained fork installed as ",[68,1387,312],{},". Its Python import is still ",[68,1390,1391],{},"faust","; do not install the original ",[68,1394,1391],{}," package for the versions and capabilities compared here.",[310,1397,1399],{"id":1398},"can-quix-streams-await-http-or-database-calls-in-the-pipeline","Can Quix Streams await HTTP or database calls in the pipeline?",[10,1401,1402,1404,1405,1408],{},[68,1403,70],{}," callbacks are synchronous. They can call HTTP or database clients, but the call blocks message processing until it returns; they cannot directly use ",[68,1406,1407],{},"await",". Use a Quix sink for batched destination writes, or choose Faust Streaming when async per-record I\u002FO is central to the processor.",[310,1410,1412],{"id":1411},"does-exactly-once-processing-cover-http-or-database-writes","Does exactly-once processing cover HTTP or database writes?",[10,1414,1415],{},"No. In this comparison, exactly-once processing applies to Kafka input offsets, framework state, and Kafka output records. HTTP calls and database writes need destination-specific idempotency or transactional handling.",[310,1417,1419],{"id":1418},"are-faust-streaming-or-quix-streams-drop-in-replacements-for-kafka-streams","Are Faust Streaming or Quix Streams drop-in replacements for Kafka Streams?",[10,1421,1422],{},"No. They provide similar concepts, including windows, state, and Kafka-backed recovery, but their APIs, state formats, configuration, and processing behavior differ. Migrating an existing Kafka Streams application requires porting the topology and rebuilding or replaying its state.",[29,1424,1426],{"id":1425},"conclusion","Conclusion",[10,1428,1429],{},"For a new stateful Kafka pipeline, start with Quix Streams, especially when you need joins, Schema Registry, or built-in connectors. Choose Faust Streaming for asyncio-based processors that need direct network I\u002FO and do not require built-in Schema Registry support or connectors.",[10,1431,1432,1433,1436],{},"Choose by technical fit first, then use repository activity and maintenance signals to compare long-term risk. If records can be processed independently and you do not need framework-managed state, windows, or joins, compare ",[13,1434,1435],{"href":25},"Python Kafka clients"," instead.",[10,1438,1439],{},"This guide is maintained by the team behind Kafma.",[1441,1442,1443],"style",{},"html pre.shiki code .sLaUg, html code.shiki .sLaUg{--shiki-default:#FFFFFF}html pre.shiki code .sUqoa, html code.shiki .sUqoa{--shiki-default:#E7E8EA}html pre.shiki code .sJl8Q, html code.shiki .sJl8Q{--shiki-default:#83CFAE}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sifj9, html code.shiki .sifj9{--shiki-default:#8A8F98}",{"title":800,"searchDepth":879,"depth":879,"links":1445},[1446,1447,1448,1452,1459,1464,1470],{"id":31,"depth":856,"text":32},{"id":122,"depth":856,"text":123},{"id":192,"depth":856,"text":193,"children":1449},[1450,1451],{"id":312,"depth":879,"text":93},{"id":344,"depth":879,"text":63},{"id":402,"depth":856,"text":403,"children":1453},[1454,1455,1456,1457,1458],{"id":409,"depth":879,"text":410},{"id":466,"depth":879,"text":467},{"id":565,"depth":879,"text":566},{"id":689,"depth":879,"text":690},{"id":754,"depth":879,"text":755},{"id":775,"depth":856,"text":776,"children":1460},[1461,1462,1463],{"id":789,"depth":879,"text":790},{"id":1260,"depth":879,"text":1261},{"id":1330,"depth":879,"text":1331},{"id":1377,"depth":856,"text":1378,"children":1465},[1466,1467,1468,1469],{"id":1381,"depth":879,"text":1382},{"id":1398,"depth":879,"text":1399},{"id":1411,"depth":879,"text":1412},{"id":1418,"depth":879,"text":1419},{"id":1425,"depth":856,"text":1426},"2026-08-13","Compare Faust and Quix Streams for Python Kafka stream processing — verified capabilities, GitHub metrics, and scenario recommendations.","md",{},null,"\u002Fblog\u002Fkafka-streams-in-python",false,{"title":5,"description":1472},{"loc":1476},"blog\u002Fkafka-streams-in-python",[1482,1483],"Kafka Stream Processing","Python","XBH1j0Bx-gP0zxK4TInsu3B95dIfH3SG3vwcMo1nt14",1786955621493]