Skip to main content

Journals API

The Journals API lets you programmatically access and manage journals from your Opennote account. Create, edit, and organize your study materials through code. Note: Your API key only has access to journals you’ve created. OAuth support for accessing user journals in third-party apps is coming soon.

Available Operations

The Journals API provides the following endpoints:

Core Operations

  • Create Journal - Start a new blank journal
  • List Journals - Get all your journals with pagination support
  • Get Content - Retrieve full journal content in markdown format
  • Rename Journal - Change a journal’s title
  • Delete Journal - Remove a journal (returns deleted data for recovery)

Advanced Operations

  • Import from Markdown - Create a journal from existing markdown content
  • Edit and Build Journal - Modify journal structure with create/update/delete operations
  • Get Model Info - Access the ProseMirror JSON representation
The full API reference can be found here.

Quick Start

from opennote import OpennoteClient

client = OpennoteClient(api_key="your_api_key")

# Create a journal
journal = client.journals.create(title="Study Notes")
print(f"Created: {journal.journal_id}")

# List all journals
journals_list = client.journals.list()
for j in journals_list.journals:
    print(f"{j.title} - {j.id}")

# Get journal content
content = client.journals.content(journal_id=journal.journal_id)
print(content.content)

# Rename journal
renamed = client.journals.rename(
    journal_id=journal.journal_id,
    title="Updated Study Notes"
)

# Delete journal
deleted = client.journals.editor.delete(journal_id=journal.journal_id)

Editing Journals

The edit endpoint allows you to modify journal content by applying operations in sequence. Each operation can create, update, or delete blocks within the journal.

Edit Operations

Use the SDK’s utility functions to build edit operations:
from opennote import OpennoteClient
from opennote.types.block_types import HeadingBlock, ParagraphBlock, Position
from opennote.util.edit_operations import create_block, delete_block, update_block

client = OpennoteClient(api_key="your_api_key")

# Get journal structure
journal_info = client.journals.editor.model_info(journal_id="your_journal_id")
last_block = journal_info.model.content[-1]

# Apply edit operations
result = client.journals.editor.edit(
    journal_id="your_journal_id",
    operations=[
        # Add a heading after the last block
        create_block(
            # You can use Position.BEFORE or Position.AFTER to specify insertion order
            position=Position.AFTER,
            reference_id=last_block.attrs.id,
            block=HeadingBlock(level=2, content="New Section")
        ),
        
        # Update an existing block by replacing it with the new `block`
        update_block(
            node_id=last_block.attrs.id,
            block=ParagraphBlock(content="Updated paragraph text")
        ),
        
        # Delete a block
        delete_block(node_id="block_id_to_delete")
    ],
    sync_realtime_state=True  # Sync changes to all connected users
)

Important Notes on Editing

  • Markdown Support: Both ParagraphBlock and HeadingBlock accept markdown strings that are automatically converted to the proper document structure
  • Sync to Collaborators: Setting sync_realtime_state: true applies changes immediately to all connected users, but (warning) disables undo functionality for those changes. This is default to true to make sure document changes aren’t overwritten by conflict resolution.
  • Operation Order: Operations are applied sequentially - earlier operations can affect later ones
  • Editing through SDK: Editing operations are quite complex in the API. The SDKs provide utility functions to help you build edit operations in a very simple way, and we recommend you prioritize using them over direct API calls.

Markdown Content Imports

Convert existing markdown content into a new journal using the import_from_markdown endpoint. This can be used to translate LLM generated content into a journal that can be edited and shared:
markdown_content = """
# Chapter 1: Introduction

This is my study guide for the exam.

## Key Concepts
- Concept 1
- Concept 2
- Concept 3
"""

imported = client.journals.editor.import_from_markdown(
    markdown=markdown_content,
    title="Imported Study Guide"  # Optional, defaults to "Imported Journal"
)
print(f"Imported journal: {imported.journal_id}")

Pagination

When listing journals, use pagination for large collections to improve performance:
# Get first page
first_page = client.journals.list()

# Get next page using token
if first_page.next_page_token:
    second_page = client.journals.list(page_token=first_page.next_page_token)

Responses

Response formats can be viewed in the API Reference.

Error Handling

The API returns standard HTTP status codes:
  • 200: Success
  • 401: Invalid or missing API key
  • 422: Invalid request parameters
  • 429: Rate limit exceeded (check Retry-After header)
  • 500: Server error
Example error response:
{
  "error": "Rate limit exceeded. Please wait 60 seconds before retrying."
}
When rate limited, the API returns a 429 status with a Retry-After header indicating when you can retry. You can always view your rate limits in the API Dashboard.

Support

Need help with the Journals API?
I