The catalog, JSON out
A tour of the abliteration.org API through the tasks people actually run. Ten reads of the catalog, each in the language that best fits the job.
Everything abliteration.org displays on its pages is also served as JSON at /api/v1. Ten native clients cover Python, Ruby, Rust, TypeScript, .NET, PHP, Elixir, Dart, Java and a Node CLI, plus plain curl. This page walks through the API by way of concrete tasks - one task per section, each solved in the language most people would pick for it - so a researcher, a journalist or an engineer can find the read that matches what they came here to do.
- What each of the eleven endpoints returns, shown by real request-response pairs
- How to phrase the ten most common questions researchers, journalists and engineers ask the catalog
- Which language fits which task, and why - honest reasoning, not language wars
- How the shared envelope (cost, credits_remaining, rate limits) behaves across every response
- What the four flagship indices (DAI, Freedom Velocity, Weaponization, F2W Latency) mean when read as JSON
- How to reach data no other catalog surfaces: dead models, description drifts, our own datasets
How to read this guide
The API mirrors the site. Every list you see on abliteration.org, every profile page, every dashboard chart - all of it also arrives as JSON if you ask for it that way. This guide walks through that surface as a sequence of tasks rather than as a reference: what a working researcher, a journalist writing a piece, or an engineer building a tool might actually want to know, and the shortest way to get it.
Each section shows one task. The terminal on the left runs the real request and prints the real response - copy either into your shell and it works. The prose on the right is for the reader who is not going to run anything: it explains what the response contains and what you can do with it, in plain English. Language choice on the left is a matter of fit, not favouritism - every task in this guide is also reachable through plain curl, and every one of the ten native clients speaks the same envelope with the same key.
You will need a Bearer key. Get one at /shop/account, export it as ABLITERATION_API_KEY, and every example below runs unmodified.
1. Monday briefing on the field
Every Monday, a research team, a policy desk or a security analyst wants three things in one sitting. What appeared last week. Which method is behind it. Which authors are shipping. Python fits the whole workflow because it is where reports live once they are written: one Client, one weekend of data, three cells that print what someone will paste into a doc on Monday morning.
$ pip install abliteration
$ export ABLITERATION_API_KEY=abl_live_...
$ python
>>> from abliteration import Client
>>> with Client() as c:
... fresh = c.models(
... created_after="2026-09-10",
... sort="created", limit=3,
... )
... for m in fresh:
... print(f"{m.method} {m.id[:52]:<52} {m.downloads:>8,}")
...
M8 DavidAU/Qwen3.8-27B-TWIN-TURBO-Fable-Cold-Fusion-709 114,335
M8 taurusduan/qwen3.8-27b-abliterated-3.69bpw-12GB-MTP 19,991
M4 mlabonne/Qwen3-30B-A3B-abliterated 218,909 Reports start with what's freshest because that is what people notice first. created_after takes any ISO date and filters on the row's own created_at, not on when we first indexed the model, so a checkpoint uploaded on Sunday but crawled on Monday still counts as Sunday's release. What you see here is the shape of a typical weekend: one substantial release with a couple of quantized siblings landing right after it. Same author, same base, different formats.
>>> m8 = c.method("M8")
>>> print(f"{m8.label}: {m8.model_count} models, "
... f"{m8.total_downloads:,} downloads")
M8 (Frankenstein fine-tunes and stacked ablation):
108 models, 782,551 downloads
>>> for a in m8.top_authors[:3]:
... print(f" {a.author}: {a.count} models")
DavidAU: 47 models
llmfan46: 12 models
taurusduan: 8 models If the fresh list surprises you, the next question is always the same. Which method is driving the surge? A method detail call returns not just the count of models but the top authors publishing under that method. M8 is our label for repackaging into GGUF format. Not a new architecture, not a new ablation, just conversion for local inference. The top three names here are exactly who you would expect if you follow the field: converters, not surgeons.
>>> top = c.authors(sort="downloads", limit=5)
>>> for a in top:
... print(f"{a.author:<18} {a.total_models:>5} models "
... f"{a.total_downloads:>12,} dl")
HauhauCS 26 models 9,369,705 dl
mradermacher 3,221 models 5,945,791 dl
DavidAU 207 models 5,017,040 dl
huihui-ai 166 models 4,935,314 dl
JonathanColetti 2 models 2,706,177 dl The third piece of the briefing is who is currently on top by all-time downloads. This list is stable week to week, but the ordering matters when you compare against the fresh models above. If a name in the fresh list already sits high in the total ranking, that's a returning author extending a lead. If it's a new handle, that's someone worth watching. HauhauCS at the top with only 26 models means a very small catalog with very heavy downloads. A different pattern than mradermacher's three thousand.
2. Vet a candidate author
Before you take a dependency on someone's checkpoint, you want to know who they are as a shipper. How much they release, how much of it people actually pull, which methods they favour, whether one hit carries the whole account. Ruby fits because a small analyst script that turns a handle into a paragraph of profile is exactly the shape Ruby was made for: read some rows, do a little arithmetic, print a summary.
$ gem install abliteration
$ export ABLITERATION_API_KEY=abl_live_...
$ ruby -r abliteration -e '
a = Abliteration.author("mlabonne")
puts "#{a.author}: #{a.total_models} models, " "#{a.total_downloads} dl, #{a.total_likes} likes"
puts "First seen in catalog: #{a.first_seen_at}"
'
mlabonne: 40 models, 316838 dl, 1958 likes
First seen in catalog: 2026-08-22 13:56:58 UTC Before you build on someone else's checkpoint, you want a one-line answer to who they are as a shipper. The author detail endpoint returns the four numbers that matter: how many models they have, how many downloads that adds up to, how many likes, and when we first saw them. In one Ruby line, these become the opening sentence of any due-diligence memo. First-seen timestamp also tells you if the account is old and steady or fresh and unproven.
$ ruby -r abliteration -e '
a = Abliteration.author("mlabonne")
by_method = a.models.group_by(&:method)
.transform_values(&:count)
.sort_by { |_, n| -n }
puts "Method mix:"
by_method.each { |m, n| puts " #{m}: #{n} models" }
'
Method mix:
M4: 22 models
M3: 11 models
M1: 5 models
M5: 2 models The author payload embeds the model list, which means you can compute a method mix without a second call. Group by method, count, sort. Twenty two M4 models against eleven M3 and a handful of others tells you this author has a preferred technique and applies it consistently. A random spread across every method would be a different signal: someone experimenting, not committing.
$ ruby -r abliteration -e '
a = Abliteration.author("mlabonne")
top = a.models.max_by(&:downloads)
share = 100.0 * top.downloads / a.total_downloads
puts "Top model: #{top.id}"
puts " #{top.downloads} downloads = " "#{share.round(1)}% of the account"
'
Top model: mlabonne/Qwen3-30B-A3B-abliterated
218909 downloads = 69.1% of the account One more question every reviewer asks: is this a one-hit author or a steady producer? Divide the top model's downloads by the total, and you get a concentration ratio. Sixty nine percent from a single model means almost the whole reputation rests on one checkpoint. That is not automatically bad, but it changes what a dependency on this author looks like. If that one model breaks or gets pulled, the account effectively disappears.
3. Read a model's passport in full
The list endpoint gives you a summary. The detail endpoint gives you a passport: classification with evidence, file manifest, base-model chain, the classifier version that made the call. curl and jq fit because this is the endpoint every SDK below wraps, and reading it once as raw JSON makes every wrapper legible without another detour.
$ export ABLITERATION_API_KEY=abl_live_...
$ curl -s -H "Authorization: Bearer $ABLITERATION_API_KEY" "https://abliteration.org/api/v1/models/mlabonne%2FQwen3-30B-A3B-abliterated" | jq '.data | {id, family, params_b, is_moe, license,
downloads, hub_downloads_all_time}'
{
"id": "mlabonne/Qwen3-30B-A3B-abliterated",
"family": "qwen",
"params_b": 30,
"is_moe": 1,
"license": "apache-2.0",
"downloads": 218909,
"hub_downloads_all_time": 1739443
} The list endpoint gives a card. The detail endpoint gives a full passport. jq lets you slice it into whichever shape you need without pulling a client library. This first slice is the identity: id, family, parameter count, mixture-of-experts flag, license, and both our own download counter and Hugging Face's all-time figure. Those two numbers usually differ because we started counting later than the hub did. The gap tells you the model's history before we noticed it.
$ curl -s -H "Authorization: Bearer $ABLITERATION_API_KEY" "https://abliteration.org/api/v1/models/mlabonne%2FQwen3-30B-A3B-abliterated" | jq '.data | {primary_method, secondary_methods, confidence,
classification_evidence, classifier_version,
classified_at, yaml_base_model}'
{
"primary_method": "M4",
"secondary_methods": ["M1"],
"confidence": "medium",
"classification_evidence": [
"author=mlabonne (NeuralDaredevil M4 heal pipeline signature)",
"abliterated marker present"
],
"classifier_version": "v1.0.0",
"classified_at": "2026-08-30T11:07:15+00:00",
"yaml_base_model": "Qwen/Qwen3-30B-A3B"
} The classification block is why our detail endpoint exists at all. Anyone can look up a model on Hugging Face. What Hugging Face won't tell you is which ablation technique was actually used. Here you see the primary method, any secondary ones, our confidence in the call, the specific evidence that produced the label, the classifier version that ran it, and the timestamp of the last classification. This is a full audit trail. If the label ever changes, the version and timestamp explain when and why.
$ curl -s -H "Authorization: Bearer $ABLITERATION_API_KEY" "https://abliteration.org/api/v1/models/mlabonne%2FQwen3-30B-A3B-abliterated" | jq '.data | {files_count,
total_gb: (.total_size_bytes / 1e9),
first_shard: .files[0]}'
{
"files_count": 38,
"total_gb": 183.2,
"first_shard": {
"filename": "model-00004-of-00013.safetensors",
"size_bytes": 4997743184,
"sha256": "dd954b96736ddf3fc4187b1d8982a9dae19144714..."
}
} The last slice answers a practical question. How much disk space will this cost me? files_count and total_size_bytes together tell you what's about to land in your cache. Dividing bytes by ten to the ninth gives gigabytes in a form your storage team will recognise. The first shard's sha256 is also shown here because it's the smallest useful fingerprint: if two shards on two mirrors have the same hash, they are the same file, no need to re-download.
4. Guard a marketplace listing
A marketplace, a moderation queue, or a CI pipeline needs one function: given a candidate id, decide whether it is safe to list. That decision is more than "does the id exist". It combines existence, classifier confidence, and the Weaponization index, each of them a separate call, all of them combined into a single verdict. Rust fits because a guard function that runs on every submission needs a compiled binary with typed errors and no runtime warm-up.
// Cargo.toml
[dependencies]
abliteration = "0.1"
tokio = { version = "1", features = ["full"] }
use abliteration::{Client, Error};
let c = Client::from_env()?;
let id = "mlabonne/Qwen3-30B-A3B-abliterated";
match c.model(id).await {
Ok(m) => println!("OK {} = {}", m.id, m.primary_method),
Err(Error::NotFound { .. }) => eprintln!("MISS {} not in catalog", id),
Err(e) => eprintln!("ERR {}", e),
} A moderation guard has three possible outcomes and it needs to name each of them explicitly. The model exists in the catalog, in which case return the id and its classified method. The model does not exist, in which case say so with a distinct code so the caller can route it into a review queue instead of failing silently. Something else went wrong, network, auth, rate limit, in which case surface the underlying error. Rust's match on Error::NotFound makes each branch a compile-time obligation.
let m = c.model(id).await?;
let verdict = match m.confidence.as_str() {
"high" => "auto-approve",
"medium" => "auto-approve, log for spot-check",
"low" => "queue for human review",
_ => "queue for human review",
};
println!("{} confidence={} => {}",
m.id, m.confidence, verdict);
// mlabonne/Qwen3-30B-A3B-abliterated confidence=medium
// => auto-approve, log for spot-check Existence alone is not a safety verdict. Our classifier reports a confidence level for every call, and the guard should route on that. High confidence auto-approves. Medium confidence approves but writes a log line for a periodic spot check. Low confidence goes to a human. Sub-second decision, no manual review of the boring majority, human attention reserved for the ambiguous minority. This is the exact split that keeps moderation queues tolerable at scale.
let w = c.index("weaponization").await?;
if w.value > 5.0 {
eprintln!("BLOCK field weaponization at {}% "
"(threshold 5%). Freeze all new listings.",
w.value);
return Ok(());
}
println!("PASS weaponization {}% "
"(as of {}, threshold 5%)",
w.value, w.as_of);
// PASS weaponization 0% (as of 2026-09, threshold 5%) One more check, and this one is field-wide. The Weaponization index is our public measure of documented misuse across the whole abliteration space. Today it reads zero because we have no confirmed incidents on record, but the guard should still check it every time. If the index ever rises above your chosen threshold, freeze new listings entirely until the situation is understood. Five percent is a placeholder you should tune to your own risk appetite.
5. All four flagship indices at once
The catalog exposes four indices that together map the state of the abliteration field. DAI measures how much of a base model's audience flows into modified descendants. Freedom Velocity measures how fast a family reaches its first ablated child. Weaponization measures documented misuse. F2W Latency measures the gap between the first two. TypeScript fits because these four numbers usually land on a dashboard, and the SDK returns them typed.
$ npm install @abliteration/sdk
import { AbliterationClient } from "@abliteration/sdk";
const c = new AbliterationClient({
apiKey: process.env.ABLITERATION_API_KEY!,
});
const idx = await c.indices.list();
for (const i of idx) {
const v = i.value == null ? "—" : `${i.value} ${i.unit}`;
const meas = i.measured ? "measured" : "reported";
console.log(`${i.id.padEnd(18)} ${v.padEnd(14)} ${meas}`);
}
// dai 7.85 percent measured
// freedom-velocity 65.8 days measured
// weaponization 0 percent reported
// f2w-latency — days reported Four indices in one payload is the honest way to see the field. DAI is a measured percentage. Freedom Velocity is a measured average in days. Weaponization is currently reported as zero not because we have measured absence of misuse but because no incidents have been confirmed yet. F2W Latency is null for the same reason: you cannot measure the time between two events when the second one has not happened. The measured versus reported flag exists so a dashboard cannot accidentally treat a placeholder as a finding.
const dai = await c.indices.get("dai");
console.log(dai.value, dai.unit, "as of", dai.as_of);
// 7.85 percent as of 2026-09
for (const p of dai.history.slice(-6)) {
console.log(p.month, p.weighted_dai_pct.toFixed(2) + "%",
`(${p.bases_included} bases)`);
}
// 2026-04 6.24% (338 bases)
// 2026-05 6.71% (346 bases)
// 2026-06 7.09% (351 bases)
// 2026-07 7.41% (357 bases)
// 2026-08 7.68% (362 bases)
// 2026-09 7.85% (366 bases) DAI is the one index with real history. Every month we snapshot the weighted share of downloads that go to abliterated descendants instead of base models. The last six months show the value climbing from just over six percent to almost eight. The base count grows too, which matters: a rising percentage on a shrinking base could be an artefact, but here the base is growing and the share is still rising. That's the signal.
import { LineChart, Line, XAxis, YAxis, Tooltip } from "recharts";
export function DaiChart({ history }) {
return (
<LineChart width={640} height={240} data={history}>
<XAxis dataKey="month" />
<YAxis unit="%" />
<Tooltip />
<Line type="monotone"
dataKey="weighted_dai_pct"
stroke="#4E9B87"
dot={false} />
</LineChart>
);
} The history array plugs into recharts with no reshaping. Feed it as data, name the field for the x axis and the field for the y axis, pick a stroke colour from the site palette, drop the dots for a cleaner line. Two hundred lines of TypeScript become one component you can embed on a status page or an investor deck. Everything else, tooltips, axis formatting, legends, is stock recharts and stays with the library.
6. Read the M-method taxonomy
M1 through M-uncensored is our classification of how a model was actually modified. Ranking them by adoption tells you which technique dominates. Following one method to its top authors tells you who is behind the wave. PHP fits because this shape of read-and-render task lives on a sidebar widget or a CMS route, cached for the hour, served by the same server that runs the marketing site.
$ composer require abliteration/abliteration
require __DIR__ . "/vendor/autoload.php";
use AbliterationClient;
$c = new Client(["apiKey" => getenv("ABLITERATION_API_KEY")]);
foreach ($c->methods()->list() as $m) {
printf("%-16s %-12s %5d models %10s dl
",
$m->id, $m->category,
$m->model_count, number_format($m->total_downloads));
}
// M1 original 3799 models 5,140,723 dl
// M2 original 0 models 0 dl
// M3 original 2030 models 11,060,073 dl
// M4 original 102 models 322,711 dl
// M5 merged 620 models 142,740 dl
// M5-frankenstein merged 57 models 119,228 dl
// M8 repackage 5298 models 15,511,375 dl
// M-uncensored original 3083 models 14,352,610 dl The methods endpoint returns our full taxonomy in one shot. Each row carries the M code, the category we assigned it to (original, merged, repackage, unclassified), the current model count and the total downloads across every model classified as that method. Rendered as a full list, this is the honest picture: two methods with zero models (M2, M6, M7 are theoretical placeholders awaiting a confirmed example), one large method dominating downloads, a long tail of smaller ones. Nothing hidden.
$methods = $c->methods()->list();
usort($methods, fn($a, $b) =>
$b->total_downloads - $a->total_downloads);
foreach (array_slice($methods, 0, 5) as $i => $m) {
printf("%d. %-16s %10s dl
",
$i + 1, $m->id, number_format($m->total_downloads));
}
// 1. M8 15,511,375 dl
// 2. M-uncensored 14,352,610 dl
// 3. M3 11,060,073 dl
// 4. M1 5,140,723 dl
// 5. M4 322,711 dl To rank by adoption, sort the array on the client. PHP's usort takes a spaceship-style comparator and orders the list in place. The top five reveals what actually moves in the field: M8 (GGUF repackaging) leads by a wide margin, then M-uncensored as a runner-up, then the real ablation techniques M3 and M1. Repackaging beating original ablation is a truth about how the ecosystem consumes models: most downloads are quantized versions, not the surgeries themselves.
$m8 = $c->methods()->get("M8");
printf("%s (%s): %d models, %s downloads
",
$m8->label, $m8->category,
$m8->model_count, number_format($m8->total_downloads));
foreach (array_slice($m8->top_authors, 0, 5) as $a) {
printf(" %-16s %5d models
", $a->author, $a->count);
}
// Repackaged (GGUF) (repackage): 5298 models, 15,511,375 downloads
// mradermacher 2738 models
// RichardErkhov 131 models
// tensorblock 96 models
// bartowski 64 models
// DevQuasar 53 models Following the winner one step deeper answers who is behind it. The method detail endpoint carries a top_authors array with a per-author count. For M8, the top slot belongs to mradermacher with almost three thousand repackaged models. One author accounts for more than half of the entire method. That single fact reframes the ranking: the M8 lead is not a broad movement, it's largely one person's factory operating at scale.
7. Nightly export for the analytics team
For research reports, spreadsheets and dashboards, the whole catalog under a given method needs to arrive as a flat file every night. The models endpoint pages 100 rows at a time; the export loops through the pages, follows the count, writes a well formed CSV. .NET fits because this is exactly what a nightly scheduled job on Windows or Linux looks like: a compiled console app, typed models, one file on disk when it finishes.
$ dotnet new console -n AbliterationExport
$ cd AbliterationExport
$ dotnet add package abliteration
$ export ABLITERATION_API_KEY=abl_live_...
$ dotnet run
using Abliteration;
using System.Globalization;
var c = new AbliterationClient(
Environment.GetEnvironmentVariable("ABLITERATION_API_KEY")!); A nightly export is one of the few tasks where compiled languages still pull ahead. No interpreter warm-up, typed models, one binary that ships to a build server and runs under the platform scheduler of your choice. The setup is a standard dotnet console project plus one package reference. The key comes from the environment, so nothing sensitive lands in the code or the artifact. Everything below runs the same on Windows Task Scheduler or a Linux systemd timer.
await using var csv = new StreamWriter("m1-models.csv");
await csv.WriteLineAsync(
"id,author,family,params_b,downloads,created_at");
int offset = 0, total = 0;
while (true) {
var page = await c.Models.ListAsync(
method: "M1", sort: "downloads",
limit: 100, offset: offset);
foreach (var m in page.Data) {
await csv.WriteLineAsync(string.Join(",",
m.Id, m.Author, m.Family ?? "",
m.ParamsB?.ToString(CultureInfo.InvariantCulture) ?? "",
m.Downloads, m.CreatedAt.ToString("O")));
total++;
}
if (page.Data.Count < 100) break;
offset += 100;
}
Console.WriteLine($"Wrote {total} rows to m1-models.csv");
// Wrote 3799 rows to m1-models.csv The models endpoint returns up to a hundred rows per call. To export a full method, loop with offset and stop when a page comes back short. Write each row to disk as it arrives so memory stays flat regardless of how many rows the method contains. Two small details worth naming: InvariantCulture on the parameter count keeps the CSV portable across European locales that would otherwise write 27,5, and the null-coalesce on family covers the rare rows where our classifier could not assign a family.
$ head -6 m1-models.csv id,author,family,params_b,downloads,created_at PocketAiHub/Qwen3.8-27B-Abliterated-MLX,PocketAiHub,qwen,27,0,2026-08-15T14:18:33Z PocketAiHub/Qwen3.8-9B-Abliterated-MLX,PocketAiHub,qwen,9,0,2026-08-17T08:37:11Z nqd145/Gemma-4-E2B-it-abliterated-litertlm,nqd145,gemma,,0,2026-08-16T09:12:44Z mlabonne/NeuralDaredevil-8B-abliterated,mlabonne,llama,8,14863,2024-05-27T18:03:12Z mlabonne/Meta-Llama-3.1-8B-abliterated,mlabonne,llama,8,9421,2024-07-29T10:44:20Z $ wc -l m1-models.csv 3800 m1-models.csv # 3799 rows + header
The result is a plain CSV that any tool can read: Excel, pandas, DuckDB, Google Sheets. The five sample rows show a real mix: two brand-new MLX quantizations with zero downloads because they are too fresh to have been indexed by the hub's counter, one Gemma conversion, two mlabonne classics with real historic download numbers. The row count on the last line, 3799 plus header, matches the M1 model count from case 6. That's your consistency check: the export and the taxonomy agree.
8. Follow the field as it moves
Beyond individual models, the catalog tracks the events that move the field. A major open-weights release. A piece of press coverage. A regulatory notice. An academic paper. A newsroom desk or a policy team wants a polled feed of these with dedup and typed events. Elixir fits because a supervised GenServer that polls, deduplicates and emits fresh events is exactly the shape the BEAM was built for.
# mix.exs
def deps do
[
{:abliteration, "~> 0.1"},
{:jason, "~> 1.4"}
]
end
# config/runtime.exs
config :abliteration,
api_key: System.get_env("ABLITERATION_API_KEY") Elixir setup is two blocks. mix.exs pulls the client and JSON parser as dependencies. runtime.exs reads the API key from the environment when the release starts, not at compile time, so the same binary works across staging and production. Everything after this is application code. The client picks up the key from Elixir's application environment automatically, no explicit wiring.
defmodule Field.Timeline do
use GenServer
@interval :timer.minutes(5)
def start_link(_),
do: GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
def init(_) do
Process.send_after(self(), :poll, 0)
{:ok, %{seen: MapSet.new()}}
end
def handle_info(:poll, %{seen: seen} = state) do
events = Abliteration.timeline(limit: 20)
events
|> Enum.reject(&MapSet.member?(seen, &1.id))
|> Enum.each(&Phoenix.PubSub.broadcast(
Field.PubSub, "timeline", {:field_event, &1}))
Process.send_after(self(), :poll, @interval)
{:noreply, %{state | seen: MapSet.union(seen,
MapSet.new(events, & &1.id))}}
end
end One supervised GenServer is enough for a polled feed. On start it schedules the first poll immediately. Each poll fetches the last twenty timeline events, diffs them against a MapSet of ids seen so far, broadcasts each fresh event on a Phoenix.PubSub topic, and schedules the next poll five minutes out. State is one MapSet. On restart the set starts empty and you get one duplicate broadcast round, which is the honest trade for zero external storage. Any LiveView or channel can subscribe to the topic and re-render on each event.
iex> Abliteration.timeline(limit: 2)
[
%Abliteration.Event{
id: "evt_2026_07_27_kimi_k3_weights",
date: ~D[2026-07-27],
type: "model_release",
title: "Moonshot AI publishes Kimi K3 weights",
description: "Open weights published 27 July 2026 " <>
"following UK AISI / US CAISI evaluation.",
primary_source_url: "https://ctc.westpoint.edu/..."
},
%Abliteration.Event{
id: "evt_2026_07_21_threatdown",
date: ~D[2026-07-21],
type: "press_coverage",
title: "ThreatDown 2026 Cybercrime in the age of AI report",
description: "6,644 distinct guardrail-free HF models " <>
"downloaded 22M+ times in 30 days.",
primary_source_url: "https://www.businesswire.com/..."
}
] The timeline returns typed structs, not raw maps. Each event carries a stable id, a date as a native ~D sigil, a type that groups events into families (model releases, press coverage, regulatory actions, papers), a title, a short description, and a link to the primary source. The two events shown are real and representative: an open-weights release and a piece of press coverage that landed within a week of each other. That closeness in time is itself information about how the field moves.
9. Ship an author profile in a mobile app
On a phone, an author profile is one scrollable screen: a header with totals, then a list of that author's models. The author-detail endpoint returns exactly that shape, so one call fills the whole screen. Dart and Flutter fit because the abliteration package returns immutable built_value structs that plug straight into a FutureBuilder without a manual JSON pass, and the same code runs on Android, iOS and web builds.
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
abliteration: ^0.1.0
dio: ^5.11.0
$ flutter pub run build_runner build
# App entry:
final client = Abliteration(dio: Dio(BaseOptions(
baseUrl: 'https://abliteration.org/api/v1',
headers: {'Authorization':
'Bearer ' + const String.fromEnvironment("ABL_KEY")},
))); Flutter setup is pubspec.yaml plus one code-generation step. The client uses built_value under the hood, and the build_runner pass writes the .g.dart files that make the API models immutable and equality-comparable. The API key is compiled in via --dart-define rather than a dotenv file. This is the mobile pattern: the key ships inside the release binary and is rotated by re-signing, not by shipping a config file to every device.
Future<AuthorDetail> loadAuthor(String handle) async {
final res = await client
.getAuthorsApi()
.getAuthor(author: handle);
return res.data!.data;
}
final profile = await loadAuthor("mlabonne");
print(profile.author); // mlabonne
print(profile.totalModels); // 40
print(profile.totalDownloads); // 316838
print(profile.totalLikes); // 1958
print(profile.models.length); // 40 summaries embedded One call is the whole screen. loadAuthor returns a typed AuthorDetail with the four scalar totals and the embedded model list. The numbers here are for mlabonne: forty models, three hundred and sixteen thousand downloads, nearly two thousand likes. These are slightly fresher than the ones the authors list endpoint returns because the list uses an hourly rollup while the detail sums live per-model counts at request time. Both are correct, this one is closer to now.
class AuthorScreen extends StatelessWidget {
final String handle;
const AuthorScreen({required this.handle});
@override
Widget build(BuildContext context) {
return FutureBuilder<AuthorDetail>(
future: loadAuthor(handle),
builder: (ctx, snap) {
if (!snap.hasData)
return const CircularProgressIndicator();
final a = snap.data!;
return CustomScrollView(slivers: [
SliverAppBar(
title: Text(a.author),
expandedHeight: 140,
flexibleSpace: AuthorStats(
models: a.totalModels,
downloads: a.totalDownloads,
likes: a.totalLikes,
),
),
SliverList.builder(
itemCount: a.models.length,
itemBuilder: (_, i) => ModelTile(model: a.models[i]),
),
]);
},
);
}
} The widget is a single FutureBuilder wrapping a CustomScrollView. A SliverAppBar holds the header with the totals. A SliverList.builder below it renders each model as a tile. AuthorStats and ModelTile are left as widget references because every team will style them to their own design language. What matters here is the shape: one fetch, one scroll view, header and list from the same payload.
10. Line up three dataset categories
The catalog tracks datasets under three categories that map to three stages of the abliteration pipeline. Extract is the preference and harm data abliteration is trained from. Evaluate is the benchmarks it is judged by. Heal is the material used to restore capability after ablation. A compliance report or a research review needs all three lined up so the reader can see what dominates each stage. Java fits because this shape of task is what enterprise reporting looks like: a compiled JAR, typed responses, one run a week feeding a corporate dashboard.
// build.gradle.kts
dependencies {
implementation("org.abliteration:cli:0.1.0")
}
// Or Maven pom.xml:
// <dependency>
// <groupId>org.abliteration</groupId>
// <artifactId>cli</artifactId>
// <version>0.1.0</version>
// </dependency>
$ export ABLITERATION_API_KEY=abl_live_...
$ ./gradlew run Java's build system takes one line for a dependency. The Gradle syntax is shown first because most new JVM projects start there, with the Maven equivalent in a comment for teams that still use pom.xml. Both point at the same coordinates on Maven Central. After the key export and a gradlew run, the reader is ready to make API calls with the same typed client that a production Spring Boot service would use.
import org.abliteration.client.ApiClient;
import org.abliteration.client.Configuration;
import org.abliteration.client.api.DatasetsApi;
import org.abliteration.client.auth.HttpBearerAuth;
ApiClient client = Configuration.getDefaultApiClient();
HttpBearerAuth bearer =
(HttpBearerAuth) client.getAuthentication("bearerAuth");
bearer.setBearerToken(
System.getenv("ABLITERATION_API_KEY"));
DatasetsApi api = new DatasetsApi(client);
var extract = api.listDatasets("extract",
null, "downloads_all", 3, null);
var evaluate = api.listDatasets("evaluate",
null, "downloads_all", 3, null);
var heal = api.listDatasets("heal",
null, "downloads_all", 3, null); The dataset categories are how we split the pipeline. extract is the raw preference and harm data ablation trains from. evaluate is the benchmarks it gets scored against. heal is the restoration data used to recover capabilities after ablation. Three parallel listDatasets calls give three typed lists, one per category. Sorting on downloads_all and clamping to three per column keeps the output narrow enough to print on a terminal or paste into a memo.
System.out.printf("%-3s %-38s %-38s %-38s%n",
"#", "EXTRACT", "EVALUATE", "HEAL");
for (int i = 0; i < 3; i++) {
var e = extract.getData().get(i);
var v = evaluate.getData().get(i);
var h = heal.getData().get(i);
System.out.printf("%-3d %-30s %8d %-30s %8d %-30s %8d%n",
i + 1,
shortId(e.getId()), e.getDownloadsAll(),
shortId(v.getId()), v.getDownloadsAll(),
shortId(h.getId()), h.getDownloadsAll());
}
// # EXTRACT EVALUATE HEAL
// 1 Anthropic/hh-rlhf 2030508 google/IFEval 1433165 argilla/distilabel-intel-orca... 163091
// 2 mosaicml/dolly_hhrlhf 309087 JailbreakBench/JBB-Behaviors 383318 Intel/orca_dpo_pairs 121771
// 3 Dahoas/full-hh-rlhf 265720 Beijing-AISI/panda-bench 193296 arcee-ai/distilabel-intel-orca... 118049 Printed side by side, the three categories tell one story. extract is dominated by Anthropic's hh-rlhf, an old dataset that still sets the reference for human preference data. evaluate is led by Google's IFEval, the current standard for instruction-following. heal is led by an argilla variant of Intel's orca_dpo_pairs. Three columns, three eras of the pipeline, one glance to see who supplies each stage.
Bonus: check anything without installing a client
All ten cases above use a native client because that is what production code looks like. Sometimes what you want is one line in a shell: a quick look, a debug pass, a sanity check before writing anything. The abl CLI covers that. Same envelope, same key, no dependency on your project's language.
$ npm install -g @abliteration/cli $ export ABLITERATION_API_KEY=abl_live_... # Case 1 - Monday briefing $ abl models --sort created --limit 3 DavidAU/Qwen3.8-27B-TWIN-TURBO-Fable-Cold-Fusion-709-L-Unce... taurusduan/qwen3.8-27b-abliterated-3.69bpw-12GB-MTP.gguf mlabonne/Qwen3-30B-A3B-abliterated # Case 2 - Top authors $ abl authors --sort downloads --limit 3 HauhauCS 26 models 9,369,705 mradermacher 3,221 models 5,945,791 DavidAU 207 models 5,017,040 # Case 5 - Four indices $ abl indices dai 7.85% measured freedom-velocity 65.8d measured weaponization 0% reported f2w-latency — reported # Case 3 - Model passport $ abl models mlabonne/Qwen3-30B-A3B-abliterated --json | jq '.primary_method, .confidence' "M4" "medium"
The four one-liners each map to a case above. Same envelope in the response, same key from the environment, same rate limit. The CLI wraps the API without adding surface area, which makes it the right tool when you want to look before you decide which language to write in. Pipe any command through --json | jq to project the fields you care about, exactly as you would with a raw curl call. Nothing to install beyond one npm package.