A small public API that returns one assembled anime shitpost as a finished
PNG. Selection is random by default. One filter exists: tag, which
narrows the images to one or more anime series. Nothing else is filtered and
there is no personalisation.
This URL responds with image/png and nothing else:
https://pbflow.de/api?k=mnbxkAvCrP_1XLRNu8FxWu6G
The response body is the PNG itself.
https://pbflow.de/api?k=mnbxkAvCrP_1XLRNu8FxWu6G&format=json
{"ok":true,
"id":"...",
"image_url":"https://pbflow.de/output/<id>.png",
"page_url":"https://pbflow.de/output/<id>",
"template_id":137,
"image_ids":[412,1987],
"matched_on":[],
"ignored":[],
"catalog_version":1,
"expires_at":"..."}
image_url points at the same rendered PNG and can be fetched or
linked. Images are removed 24 hours after they are created, so a stored URL
stops resolving after that.
tag takes a comma-separated list of series. The images are then
drawn only from those series:
https://pbflow.de/api?k=mnbxkAvCrP_1XLRNu8FxWu6G&tag=nagatoro,one-piece&format=json
Two fields in the response say what happened:
{"...":"...",
"template_id":58,
"image_ids":[1204],
"matched_on":["nagatoro"],
"ignored":["one-piece"],
"catalog_version":1}
matched_on — the series that actually contributed at least one
image to this post. Not the series you asked for: if a series matched but none
of its images made it into a slot, it is not listed.ignored — the values that resolved to nothing and were dropped.
The request still succeeds.In the example above, one-piece is not in the catalogue, so it
was dropped. That is what a guessed value looks like.
/api/facets is the only valid
source for tag values. It needs no token:
https://pbflow.de/api/facets
{"ok":true,
"catalog_version":1,
"series":[{"id":"nagatoro","label":"Don’t Toy With Me, Miss Nagatoro","image_count":41},
{"id":"k-on","label":"K-On!","image_count":12}]}
Do not guess values. Read the list, then send the
id strings from it. A value that is not on the list cannot match
anything — there is no fuzzy matching, no partial matching and no correction of
typos. The response is cacheable for five minutes; fetch it once per session,
not once per request — it counts against the same rate limit as
/api.
image_count is the number of images behind that series. It is
worth reading: a series with a single image can only be combined with the
single-image layouts, so posts for it will look less varied.
A few common alternative spellings resolve as well, but only the
id values are part of the contract. Use those.
tag must be a plain string. ?tag=a&tag=b, a
number, a list or a nested object in a POST body is rejected with
400 bad_parameter_type. Write
tag=a,b instead.Tags are thematic: they name a series or franchise. They are not a style, a
mood or a kind of humour. tag=deadpan does not exist and is
ignored — there is no humour axis in this data, and no filter can invent one.
character is still accepted and still has no effect. It does not
narrow anything.
If nothing resolves, the API does not fail. It returns 200 with
an ordinary post, matched_on: [], the dropped values in
ignored, and a note saying the selection was random.
Treat that as "your tags did not apply", not as a failed request.
The same holds if catalog_version comes back as
null and /api/facets returns an empty
series list: the catalogue is not loaded, every value lands in
ignored, and results are random until it is back.
Authorization header works the same way as the k
parameter, for clients that can set headers.429 — over the limit. The body carries
retry_after in seconds.503 with error: "service_disabled" — the operator
has switched the API off. /api/facets answers the same way.401 — the token is missing, revoked or expired.error field and a fix
field describing what would make the request work.