2  Quickstart

A walkthrough of the zulipdata library’s core API: authenticating, listing public channels, pulling channel messages, and shaping them into a tablecloth dataset. Run this notebook end-to-end to confirm your setup works.

Credentials are read from ZULIP_EMAIL / ZULIP_API_KEY env vars or ~/.zuliprc — see Zulip’s API keys documentation for how to obtain them.

(ns zulipdata-book.quickstart
  (:require
   ;; Zulipdata client -- Zulip REST API wrapper
   [scicloj.zulipdata.client :as client]
   ;; Zulipdata pull -- paginated, cached channel history
   [scicloj.zulipdata.pull :as pull]
   ;; Zulipdata views -- tablecloth projections of raw messages
   [scicloj.zulipdata.views :as views]
   ;; Kindly -- notebook rendering protocol
   [scicloj.kindly.v4.kind :as kind]
   ;; Tablecloth -- dataset manipulation
   [tablecloth.api :as tc]))

Authenticating

client/whoami calls /users/me and returns a summary of the authenticated identity:

(def me (client/whoami))
me
{:email "user138175@clojurians.zulipchat.com",
 :full-name "Daniel Slutsky",
 :user-id 138175,
 :is-bot false,
 :is-admin true,
 :role 100}

Listing channels

pull/public-channel-names returns every channel the bot can read — both fully public (login-gated) and the smaller subset that is web-public (readable without logging in).

(def public-channels (pull/public-channel-names))
(count public-channels)
178

A prefix:

(take 5 (sort public-channels))
("Cursive" "Defi" "Disorganized" "Emacs" "Graalvm")

pull/web-public-channel-names returns just the web-public subset. Throughout this book we draw demo data from these channels so that message content can be shown without leaking anything login-gated.

(def web-public (pull/web-public-channel-names))
web-public
["announce"
 "beginners"
 "bubble-up"
 "calva"
 "clojars"
 "clojure"
 "clojure-europe"
 "clojure-ohio"
 "clojure-uk"
 "clojurecivitas"
 "clojurescript"
 "events"
 "general"
 "gratitude"
 "honeysql"
 "jobs"
 "news-and-articles"
 "off-topic"
 "project-announcements"
 "scicloj-webpublic"
 "slack-archive"
 "sql"
 "std.lang-dev"
 "windows-clojure"
 "xtdb"
 "zulip"]

Pulling messages from one channel

pull/pull-channels! walks forward through a list of channels in cached windows. The first run populates the disk cache; subsequent runs are served from cache. We pull clojurecivitas, a web-public channel.

(def pulled
  (pull/pull-channels! ["clojurecivitas"]))
(def message-count
  (get-in pulled ["clojurecivitas" :message-count]))
message-count
273

Flatten the cached windows into a single sequence of raw messages:

(def raw-messages
  (pull/all-messages (get pulled "clojurecivitas")))
(count raw-messages)
273

A single raw message — one map per Zulip message, with sender, topic, content, timestamps, reactions, and edit history:

(first raw-messages)
{:display_recipient "clojurecivitas",
 :content
 "**Public** channel created by @_**Timothy Pratley|550430**. **Description:**\n```` quote\n*No description.*\n````",
 :sender_id 100006,
 :client "Internal",
 :submessages [],
 :type "stream",
 :sender_realm_str "zulipcore",
 :stream_id 528764,
 :content_type "text/x-markdown",
 :id 538932511,
 :is_me_message false,
 :sender_email "notification-bot@zulip.com",
 :sender_full_name "Notification Bot",
 :recipient_id 1681040,
 :timestamp 1757619820,
 :flags ["read" "historical"],
 :subject "channel events",
 :reactions [],
 :topic_links [],
 :avatar_url
 "https://static.zulipchat.com/static/images/static_avatars/notification-bot.36f721bad3d0.png"}

Building a timeline view

views/messages-timeline projects raw messages into a tablecloth dataset with one row per message and simple-valued columns only:

(def timeline (views/messages-timeline raw-messages))
(-> timeline
    (tc/order-by :instant :desc))

_unnamed [273 13]:

:instant :content :last-edit-ts :client :channel :stream-id :edited :content-length :id :sender :sender-id :timestamp :subject
2026-05-02T18:42:10Z These days, we are exploring Babashka for data analysis and using Babqua for interactive data visualization. This time, we are looking into @**Gert Goet**’s Clojure Events Calendar Feed. website clojurecivitas 528764 false 186 592473831 Daniel Slutsky 138175 1777747330 Datavis in Babashka: analysing our calendar feed
2026-05-02T18:39:21Z Datavis in Babashka: analysing our calendar feed ZulipZapierApp clojurecivitas 528764 false 130 592473657 Civitas 942591 1777747161 Datavis in Babashka: analysing our calendar feed
by Daniel Slutsky
2026-05-02T16:31:38Z lovely :smile: ZulipElectron clojurecivitas 528764 false 14 592462084 Timothy Pratley 550430 1777739498 Hello, Babashka
2026-05-02T15:01:33Z Dark mode in mermaid works now, thanks @**Timothy Pratley**. website clojurecivitas 528764 false 60 592454894 Daniel Slutsky 138175 1777734093 Hello, Babashka
2026-05-01T17:15:48Z https://github.com/scicloj/babqua/issues/1 website clojurecivitas 528764 false 42 592324446 Daniel Slutsky 138175 1777655748 Hello, Babashka
2026-05-01T17:13:13Z :thumbs_up: ZulipElectron clojurecivitas 528764 false 11 592323987 Timothy Pratley 550430 1777655593 Hello, Babashka
2026-05-01T17:12:20Z Kindly is handled in a very ad-hoc way at the moment: website clojurecivitas 528764 false 130 592323846 Daniel Slutsky 138175 1777655540 Hello, Babashka
https://github.com/scicloj/babqua/blob/6f9f703/_extensions/bb/runtime.bb#L74
2026-05-01T17:11:14Z Oh, thanks. website clojurecivitas 528764 false 11 592323688 Daniel Slutsky 138175 1777655474 Hello, Babashka
2026-05-01T17:08:06Z In dark mode the mermaid connection lines are not very visible. This might be fixed by adding the class “mermaid” (which is what happens here: https://clojurecivitas.org/scicloj/clay/mermaid.html) ZulipElectron clojurecivitas 528764 false 911 592323242 Timothy Pratley 550430 1777655286 Hello, Babashka
“mermaid” and “js-plotly-plot” classes currently have their hue inverted for this purpose:
https://github.com/ClojureCivitas/clojurecivitas.github.io/blob/68b5e81b4733aea601d0e5822eb88fa9b2adbdf6/site/styles.scss#L33
Alternatively this could be addressed by setting a background or using other properties to identify when hue should be inversed. In any case dark mode is just a minor issue but mentioning some details if you are curious.
The tree of babashka icons looks really cool!
How does kindly work? Is it using Clay or kindly-render or a custom implementation?
Should we also hope for a Clojurqua? (Maybe Babashqua fills this need)
Looking forward to hearing more details at the real-world-data meeting.
2026-05-01T16:51:39Z amazing! ZulipElectron clojurecivitas 528764 false 8 592320484 Timothy Pratley 550430 1777654299 Hello, Babashka
2025-09-13T12:49:06Z Despite the understandable habit of “Whereof one cannot speak, thereof one must be silent” when it comes to licenses, I think it is worth mentioning that the Eclipse license is not necessarily the best choice for Clojure libraries. For example, XTDB by Juxt is under a Mozilla license. I’d like to point to a 2021 Juxt-blogpost titled “Prefer the MIT License, The EPL is not a Sensible Default”: website clojurecivitas 528764 false 1253 539269032 Markus Agwin 360348 1757767746 licenses
quote | | | | | | | | | | | | | | It is common to see the following text at the bottom of READMEs in libraries from the Clojure ecosystem: | | | | | | | | | | | | | | | | | | | | | | | | | | | | Distributed under the Eclipse Public License, the same as Clojure. | | | | | | | | | | | | | | | | | | | | | | | | | | | | There is an implication worthy of deconstruction embedded in such a sentence. When we declare “we do X, the same as Clojure” what we’re really saying is “we use X because Clojure uses X.” This sort of thinking makes sense if our constraints mirror Clojure’s constraints. Unfortunately, it’s very unlikely that they do. | | | | | | | | | | | | | |
Maybe it is worthwhile to contact Juxt for an updated blog post to be published on the Clojure Civitas website. After all, civitas means “citizens united by law” and it is certainly worthwhile to state that the Eclipse EPL is not the law that is meant to form Clojure Civitas.
2025-09-12T16:00:16Z Hi thanks for all the guidance in the clojurescript thread. ZulipElectron clojurecivitas 528764 false 767 539107122 Timothy Pratley 550430 1757692816 A browser based REPL+chart with Scittle Kitchen
To summarize, I think we are all interested in a Scittle driven chart based experience, but still unsure of the best way to render charts.
1. Tableplot is possibly the best option in Clojure, but currently is not available in Scittle. Some work (not Herculean) would be required to convert Tableplot source to cljc.
2. Vega is well established in this space. Possibly some work is necessary to find the nicest way to use it, and make the sizing appropriate.
3. thi.ng/geom has really compelling charts, but is a less widely used/known approach
4. emmy-viewers has entanglements that we wish to avoid.
Please let me know if there are other alternatives, and or which if any seems the most promising future.
2025-09-12T09:58:38Z @_Jarkko Saltiola|727865 said: website clojurecivitas 528764 false 463 539025921 Markus Agwin 360348 1757671118 licenses
quote | | | | | | | | | | | | | | there's no lawyers available to help | | | | | | | | | | | | | |
I think this is the reason why people stopped to participate in discussions about license. Anything that is written in forums (including my writings) is not legally binding and ultimately in vain. Every person has to decide on his own which risk to take and which to avoid.
2025-09-12T06:16:10Z I also discovered the EPL1.0 v.s. GPLv2+ incompatibility recently while I’ve been working around WordPress, particularly with a block editor (React based) Scittle integration https://codeberg.org/jasalt/wp-block-experiments. website clojurecivitas 528764 false 1528 538989269 Jarkko Saltiola 727865 1757657770 licenses
While there are various workarounds, the “ecosystem” extension/plugin marketplace gives quite strict rules for non-GPL software:
> All code, data, and images — anything stored in the plugin directory hosted on WordPress.org — must comply with the GPL or a GPL-Compatible license. Included third-party libraries, code, images, or otherwise, must be compatible.
> Calling third party CDNs for reasons other than font inclusions; all non-service related JavaScript and CSS must be included locally
This takes some fun out Clojure(Script) and similarly licensed dialects for me. All the time (2014-) I had thought EPL1.0 was just another Apache 2.0 -like license, or at least something I could do use freely around GPL licensed software.
Had some discussion about it on Slack earlier, @**borkdude** would be happy to change Scittle and possibly his other works to BSD-3 but it is not known if it’s possible due to definition of derivative work being vaque in EPL1.0 and there’s no lawyers available to help.
https://clojurians.slack.com/archives/C034FQN490E/p1751214935946939
https://clojurians.slack.com/archives/C07UQ678E/p1751280671744189
2025-09-11T22:21:11Z Things I know for sure about GPL: 1757633171 website clojurecivitas 528764 true 1341 538950637 Markus Agwin 360348 1757629271 licenses
- On your laptop you can do, from a GPL standpoint, whatever you want
- It never violates the GPL to publish source code
- GPL pertains to published “object-code”. An example for an “object-code” is a binary compiled from C source-code.
- It violates the GPL to publish a single “object-code” file which is generated from several source files which are partly GPL and partly Eclipse-EPL
- Dynamically linking a GPL “object-code” to a non-GPL “Systems library” is permissable under GPL. An example for a “Systems library” is the window-system of an operating system.
The above is what I know for sure.
I do not know for sure, but I think it is fair to say:
A js-files compiled from a cljs-file (e.g. using shadow-cljs) is “object-code”. Thus it would clearly violate the GPL to publish a single file scittle.js that exposes both clojure.core (EPL) and Emmy (GPL). But we do not do that.
My wishful thinking is:
Since, in scittle-kitchen, Emmy is a plugin, Emmy is dynamically linked to a “Systems library”. The “Systems library” is the file https://cdn.jsdelivr.net/npm/scittle-kitchen/dist/scittle.js.
But to be honest, I think my argument in my wishful thinking is a very weak one in favour of a positive answer to the question whether including Emmy in scittle-kitchen is or is not GPL complient.
2025-09-11T20:40:50Z @_Daniel Slutsky|138175 changed the access permissions for this channel from Public to Web-public. Internal clojurecivitas 528764 false 139 538939857 Notification Bot 100006 1757623250 channel events
2025-09-11T19:54:57Z Where should we discuss ClojureCivitas? 1757622002 ZulipElectron clojurecivitas 528764 true 197 538933920 Timothy Pratley 550430 1757620497 Maybe this channel should not exist
One option is to enable discussion on the site itself:
https://quarto.org/docs/output-formats/html-basics.html#commenting
or in #announce>ClojureCivitas
2025-09-11T19:50:44Z @**Markus Agwin** @**Daniel Slutsky** suggesting we discuss here instead of in clojurescript as we might be getting a bit off-topic for that channel :shrug: ZulipElectron clojurecivitas 528764 false 156 538933408 Timothy Pratley 550430 1757620244 A browser based REPL+chart with Scittle Kitchen
2025-09-11T19:49:34Z #clojurescript>Looking for early feedback on Scittle Kitchen@538823252 ZulipElectron clojurecivitas 528764 false 74 538933272 Timothy Pratley 550430 1757620174 A browser based REPL+chart with Scittle Kitchen
2025-09-11T19:47:11Z #clojurescript>Looking for early feedback on Scittle Kitchen@538924975 ZulipElectron clojurecivitas 528764 false 201 538933023 Timothy Pratley 550430 1757620031 licenses
Civitas accepts many dependencies and is for writing about them. It’s not clear what (if any) license implications that has.
2025-09-11T19:43:40Z Public channel created by @_Timothy Pratley|550430. Description: Internal clojurecivitas 528764 false 110 538932511 Notification Bot 100006 1757619820 channel events
quote | | | | | | | | | | | | | | *No description.* | | | | | | | | | | | | | |
(tc/row-count timeline)
273

The dataset’s columns:

(tc/column-names timeline)
(:instant
 :content
 :last-edit-ts
 :client
 :channel
 :stream-id
 :edited
 :content-length
 :id
 :sender
 :sender-id
 :timestamp
 :subject)

Next steps

The rest of this book is one chapter per namespace:

  • The REST client — what client/whoami does internally, plus the four endpoints the library wraps.

  • Pulling and caching channels — the cache model behind pull/pull-channels!, plus options like :refresh for keeping a corpus up to date.

  • Tablecloth viewsviews/messages-timeline, views/reactions-long, views/edits-long, and views/topic-links-long.

  • Anonymized views — parallel views with sender names and topic strings replaced by stable hash keys, message content dropped — for sharing datasets without leaking identities or text.

  • Narrative — date columns, channel lifecycles, and newcomer tracking.

  • Graph views — co-membership and co-presence graphs, community detection, and rendering.

  • API Reference — every public function in one chapter.

source: notebooks/zulipdata_book/quickstart.clj