API Reference

Zines API

Complete reference for the Zines API endpoints

The Zines API provides access to ZineCore2 bibliographic records. All zine metadata follows the ZineCore2 Dublin Core Application Profile.

Endpoints

MethodEndpointDescription
GET/api/zines/List all zines (paginated)
POST/api/zines/Create new zine
GET/api/zines/{zine_id}/Get specific zine
PUT/api/zines/{zine_id}/Replace zine (all fields)
PATCH/api/zines/{zine_id}/Update zine (partial)
DELETE/api/zines/{zine_id}/Delete zine

List Zines

Get paginated list of all zine records.

Request

GET /api/zines/

Authentication: Not required (public endpoint)

Query parameters:

  • page — Page number (default: 1)
  • page_size — Results per page (default: 25, max: 100)
  • search — Full-text search across title and abstract
  • subject — Filter by subject code
  • genre — Filter by genre code
  • creator — Filter by creator agent ID
  • language — Filter by language code
  • ordering — Sort field (prefix with - for descending)

Response

{
  "count": 142,
  "next": "http://localhost:8000/api/zines/?page=2",
  "previous": null,
  "results": [
    {
      "zine_id": "zine_mutate_3_1st",
      "title": "Mutate Zine #3: Abortion Stories",
      "series_title": ["Mutate Zine"],
      "issue_designation": "#3",
      "creator": [
        {
          "agent_id": "agent_judith_arcana",
          "display_name": "Judith Arcana",
          "kind": "Person"
        }
      ],
      "subject": [
        {
          "code": "feminism",
          "label": "Feminism",
          "uri": "https://zinecore.org/v2/subjects#feminism"
        }
      ],
      "genre": [
        {
          "code": "personal-zine",
          "label": "Personal Zine",
          "uri": "https://zinecore.org/v2/genres#personal-zine"
        }
      ],
      "date": ["2024"],
      "language": ["en"],
      "rights": ["cc-by-4.0"],
      "created_at": "2024-02-25T10:30:00Z",
      "updated_at": "2024-02-25T10:30:00Z"
    }
  ]
}

Examples

Basic list:

curl http://localhost:8000/api/zines/

Search by title:

curl "http://localhost:8000/api/zines/?search=mutate"

Filter by subject:

curl "http://localhost:8000/api/zines/?subject=feminism"

Filter by genre:

curl "http://localhost:8000/api/zines/?genre=personal-zine"

Filter by creator:

curl "http://localhost:8000/api/zines/?creator=agent_judith_arcana"

Multiple filters:

curl "http://localhost:8000/api/zines/?subject=feminism&genre=personal-zine&ordering=-created_at"

Custom page size:

curl "http://localhost:8000/api/zines/?page_size=50"

Get Zine

Retrieve a specific zine by its external ID.

Request

GET /api/zines/{zine_id}/

Authentication: Not required (public endpoint)

URL parameters:

  • zine_id — Zine's external identifier (e.g., zine_mutate_3_1st)

Response

{
  "zine_id": "zine_mutate_3_1st",
  "title": "Mutate Zine #3: Abortion Stories",
  "series_title": ["Mutate Zine"],
  "issue_designation": "#3",
  "edition_statement": [],
  "alternative_title": [],
  "creator": [
    {
      "agent_id": "agent_judith_arcana",
      "display_name": "Judith Arcana",
      "kind": "Person",
      "pronouns": ["she/her"],
      "biography": "Feminist writer and activist."
    }
  ],
  "contributor": [],
  "subject": [
    {
      "code": "feminism",
      "label": "Feminism",
      "definition": "Social, political, and economic equality of the sexes.",
      "uri": "https://zinecore.org/v2/subjects#feminism"
    },
    {
      "code": "reproductive-rights",
      "label": "Reproductive Rights",
      "uri": "https://zinecore.org/v2/subjects#reproductive-rights"
    }
  ],
  "genre": [
    {
      "code": "personal-zine",
      "label": "Personal Zine",
      "uri": "https://zinecore.org/v2/genres#personal-zine"
    }
  ],
  "abstract": "Personal narratives and essays about reproductive rights and abortion access.",
  "table_of_contents": "",
  "public_notes": [],
  "publisher": [],
  "date": ["2024"],
  "physical_dimensions": "",
  "number_of_pages": "32",
  "format": ["Photocopy"],
  "binding_features": [],
  "language": ["en"],
  "place_of_publication": [],
  "coverage": [],
  "source": [],
  "relation": [],
  "rights": ["cc-by-4.0"],
  "identifier": [],
  "created_at": "2024-02-25T10:30:00Z",
  "updated_at": "2024-02-25T10:30:00Z"
}

Examples

Get zine:

curl http://localhost:8000/api/zines/zine_mutate_3_1st/

Get as JSON-LD:

curl -H "Accept: application/ld+json" \
  http://localhost:8000/api/zines/zine_mutate_3_1st/

Get as BibTeX:

curl -H "Accept: application/x-bibtex" \
  http://localhost:8000/api/zines/zine_mutate_3_1st/

Create Zine

Create a new zine record.

Request

POST /api/zines/
Content-Type: application/json
Authorization: Token YOUR_TOKEN

Authentication: Required

Body (JSON):

All fields use the write serializer format (simple IDs, not nested objects).

Required Fields

FieldTypeDescriptionExample
zine_idstringUnique external identifier"zine_mutate_3_1st"
titlestringPrimary title"Mutate Zine #3"
creatorarraystringCreator agent IDs["agent_judith_arcana"]
subjectarraystringSubject codes["feminism"]
genrearraystringGenre codes["personal-zine"]
datearraystringPublication dates["2024"]
languagearraystringLanguage codes (ISO 639-1)["en"]
rightsarraystringRights statement codes["cc-by-4.0"]

Optional Fields

FieldTypeDescriptionExample
series_titlearraystringSeries name(s)["Mutate Zine"]
issue_designationstringIssue number/label"#3"
edition_statementarraystringEdition info["First edition"]
alternative_titlearraystringSubtitles, variants["Stories of Choice"]
contributorarraystringContributor agent IDs["agent_jane_doe"]
abstractstringSummary description"Personal narratives..."
table_of_contentsstringTOC text"Introduction\nStory 1..."
public_notesarraystringPublic notes["Content warning: abortion"]
publisherarraystringPublisher agent IDs["agent_feminist_press"]
physical_dimensionsstringHeight × width"8.5 × 5.5 inches"
number_of_pagesstringPage count"32"
formatarraystringProduction method["Photocopy"]
binding_featuresarraystringBinding type["Saddle-stitched"]
place_of_publicationarraystringPublication place["Seattle, WA"]
coveragearraystringSpatial/temporal coverage["US, 2020s"]
sourcearraystringAcquisition info["Donated by author"]
relationarraystringRelated works["See also: Mutate #2"]
identifierarraystringOther identifiers["OCLC:123456"]

Request Example

curl -X POST http://localhost:8000/api/zines/ \
  -H "Authorization: Token YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "zine_id": "zine_mutate_3_1st",
    "title": "Mutate Zine #3: Abortion Stories",
    "series_title": ["Mutate Zine"],
    "issue_designation": "#3",
    "creator": ["agent_judith_arcana"],
    "subject": ["feminism", "reproductive-rights", "personal-narratives"],
    "genre": ["personal-zine"],
    "date": ["2024"],
    "language": ["en"],
    "rights": ["cc-by-4.0"],
    "abstract": "Personal narratives and essays about reproductive rights.",
    "number_of_pages": "32",
    "format": ["Photocopy"]
  }'

Response (201 Created)

Returns the full zine object with all relationships resolved (read serializer format):

{
  "zine_id": "zine_mutate_3_1st",
  "title": "Mutate Zine #3: Abortion Stories",
  "series_title": ["Mutate Zine"],
  "issue_designation": "#3",
  "creator": [
    {
      "agent_id": "agent_judith_arcana",
      "display_name": "Judith Arcana",
      "kind": "Person"
    }
  ],
  "subject": [
    {
      "code": "feminism",
      "label": "Feminism",
      "uri": "https://zinecore.org/v2/subjects#feminism"
    }
  ],
  "created_at": "2024-02-25T10:30:00Z",
  "updated_at": "2024-02-25T10:30:00Z"
}

Update Zine

Update an existing zine record.

PATCH (Partial Update)

Update only specific fields.

PATCH /api/zines/{zine_id}/
Content-Type: application/json
Authorization: Token YOUR_TOKEN

Request:

curl -X PATCH http://localhost:8000/api/zines/zine_mutate_3_1st/ \
  -H "Authorization: Token YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "abstract": "Updated description with more detail.",
    "number_of_pages": "36"
  }'

Only provided fields are updated. All other fields remain unchanged.

PUT (Full Replacement)

Replace the entire zine record.

PUT /api/zines/{zine_id}/
Content-Type: application/json
Authorization: Token YOUR_TOKEN

Request:

curl -X PUT http://localhost:8000/api/zines/zine_mutate_3_1st/ \
  -H "Authorization: Token YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "zine_id": "zine_mutate_3_1st",
    "title": "Mutate Zine #3",
    "creator": ["agent_judith_arcana"],
    "subject": ["feminism"],
    "genre": ["personal-zine"],
    "date": ["2024"],
    "language": ["en"],
    "rights": ["cc-by-4.0"]
  }'

All required fields must be provided. Omitted optional fields are cleared.

Response (200 OK)

Returns the updated full zine object.


Delete Zine

Delete a zine record.

Request

DELETE /api/zines/{zine_id}/
Authorization: Token YOUR_TOKEN

Authentication: Required

Example:

curl -X DELETE http://localhost:8000/api/zines/zine_mutate_3_1st/ \
  -H "Authorization: Token YOUR_TOKEN"

Response (204 No Content)

No response body.

Cascade behavior:

  • All holdings referencing this zine are also deleted
  • Agent records are NOT deleted (agents can exist without zines)

Field Details

zine_id

Type: string (max 255 characters) Required: Yes Unique: Yes

External unique identifier for the zine.

Naming convention: zine_{title_slug}_{issue}_{edition}

Examples:

zine_mutate_3_1st
zine_feminist_killjoy_1_2nd
zine_punk_planet_73

title

Type: string (max 500 characters) Required: Yes Repeatable: No

Primary title of the zine issue.

Examples:

"Mutate Zine #3: Abortion Stories"
"Feminist Killjoy #1"
"Punk Planet #73"

series_title

Type: array of strings (max 500 characters each) Required: No Repeatable: Yes

Name of the zine series this issue belongs to.

Examples:

["Mutate Zine"]
["Feminist Killjoy"]
[]

issue_designation

Type: string (max 100 characters) Required: No Repeatable: No

Issue number or label within a series.

Examples:

"#3"
"No. 12"
"Spring 2024"
"Vol. 2, Issue 4"

creator

Type: array of strings (agent IDs) Required: Yes Repeatable: Yes

Agent IDs of primary creators.

Write (POST/PUT/PATCH):

["agent_judith_arcana", "agent_jane_doe"]

Read (GET response):

[
  {
    "agent_id": "agent_judith_arcana",
    "display_name": "Judith Arcana",
    "kind": "Person"
  }
]

Validation:

  • All agent IDs must exist in agents table
  • At least one creator required

contributor

Type: array of strings (agent IDs) Required: No Repeatable: Yes

Agent IDs of contributors (illustrators, editors, etc.).

Same format as creator.

subject

Type: array of strings (subject codes) Required: Yes Repeatable: Yes

Subject codes from the subjects vocabulary.

Write:

["feminism", "reproductive-rights", "personal-narratives"]

Read:

[
  {
    "code": "feminism",
    "label": "Feminism",
    "uri": "https://zinecore.org/v2/subjects#feminism"
  }
]

Validation:

  • All codes must exist in subjects vocabulary
  • At least one subject required

Available codes: See /api/vocabularies/subjects/

genre

Type: array of strings (genre codes) Required: Yes Repeatable: Yes

Genre codes from the genres vocabulary.

Write:

["personal-zine", "art-zine"]

Read:

[
  {
    "code": "personal-zine",
    "label": "Personal Zine",
    "uri": "https://zinecore.org/v2/genres#personal-zine"
  }
]

Available codes: See /api/vocabularies/genres/

date

Type: array of strings Required: Yes Repeatable: Yes

Publication or creation dates.

Format: ISO 8601 or text (flexible)

Examples:

["2024"]
["2024-03"]
["2024-03-15"]
["Spring 2024"]
["c. 2020"]
["2019-2020"]

language

Type: array of strings (ISO 639-1 codes) Required: Yes Repeatable: Yes

Language codes for content language(s).

Format: 2-letter ISO 639-1 codes

Examples:

["en"]
["en", "es"]
["fr"]

Common codes:

  • en — English
  • es — Spanish
  • fr — French
  • de — German
  • zh — Chinese

Full list: See /api/geography/languages/

rights

Type: array of strings (rights codes) Required: Yes Repeatable: Yes

Rights and permissions statements.

Examples:

["cc-by-4.0"]
["all-rights-reserved"]
["public-domain"]
["unknown"]

Available codes: See /api/vocabularies/rights-status/

format

Type: array of strings (format codes) Required: No Repeatable: Yes

Production methods and formats.

Examples:

["Photocopy"]
["Risograph"]
["Offset", "Screen-printed cover"]

Available codes: See /api/vocabularies/formats/

physical_dimensions

Type: string (max 100 characters) Required: No Repeatable: No

Physical dimensions of the zine.

Format: height × width [units]

Examples:

"8.5 × 5.5 inches"
"21 × 14.8 cm"
"Half-letter"

number_of_pages

Type: string (max 50 characters) Required: No Repeatable: No

Page count.

Examples:

"32"
"24 pages"
"Unpaged"
"ca. 40"

Validation Errors

Missing Required Field

Request:

{
  "zine_id": "zine_test",
  "title": "Test Zine"
  // Missing required fields
}

Response (400 Bad Request):

{
  "creator": ["This field is required."],
  "subject": ["This field is required."],
  "genre": ["This field is required."],
  "date": ["This field is required."],
  "language": ["This field is required."],
  "rights": ["This field is required."]
}

Invalid Agent ID

Request:

{
  "creator": ["agent_nonexistent"]
}

Response (400 Bad Request):

{
  "creator": ["Agent 'agent_nonexistent' does not exist"]
}

Invalid Vocabulary Code

Request:

{
  "subject": ["invalid-subject-code"]
}

Response (400 Bad Request):

{
  "subject": ["Invalid subject codes: invalid-subject-code"]
}

Duplicate zine_id

Request:

{
  "zine_id": "zine_existing"
}

Response (400 Bad Request):

{
  "zine_id": ["Zine with this zine_id already exists."]
}

Code Examples

Python

import requests

API_BASE = "http://localhost:8000/api"
TOKEN = "YOUR_TOKEN"
headers = {
    "Authorization": f"Token {TOKEN}",
    "Content-Type": "application/json"
}

# Create zine
zine_data = {
    "zine_id": "zine_my_zine",
    "title": "My Zine",
    "creator": ["agent_me"],
    "subject": ["feminism"],
    "genre": ["personal-zine"],
    "date": ["2024"],
    "language": ["en"],
    "rights": ["cc-by-4.0"]
}

response = requests.post(
    f"{API_BASE}/zines/",
    headers=headers,
    json=zine_data
)

if response.status_code == 201:
    print(f"Created: {response.json()['title']}")
else:
    print(f"Error: {response.json()}")

# Get zine
response = requests.get(f"{API_BASE}/zines/zine_my_zine/")
zine = response.json()
print(f"Title: {zine['title']}")
print(f"Creator: {zine['creator'][0]['display_name']}")

# Update zine
response = requests.patch(
    f"{API_BASE}/zines/zine_my_zine/",
    headers=headers,
    json={"abstract": "Updated description"}
)

JavaScript

const API_BASE = "http://localhost:8000/api";
const TOKEN = "YOUR_TOKEN";

// Create zine
const zineData = {
  zine_id: "zine_my_zine",
  title: "My Zine",
  creator: ["agent_me"],
  subject: ["feminism"],
  genre: ["personal-zine"],
  date: ["2024"],
  language: ["en"],
  rights: ["cc-by-4.0"]
};

const response = await fetch(`${API_BASE}/zines/`, {
  method: "POST",
  headers: {
    "Authorization": `Token ${TOKEN}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify(zineData)
});

const zine = await response.json();
console.log(`Created: ${zine.title}`);

// Get zine
const getResponse = await fetch(`${API_BASE}/zines/zine_my_zine/`);
const fetchedZine = await getResponse.json();
console.log(`Title: ${fetchedZine.title}`);

Next Steps

Copyright ©2026 ZineCore2 Contributors,