Skip to main content
The Take Time REST API gives you programmatic access to your entire study schedule. You can automate block creation from calendar imports, pull heatmap data into personal dashboards, or build custom reminders — all through a simple HTTP API.

Common integration patterns

Every integration starts with an API key. Generate one in Settings → Developer → Generate API Key, then use it as a Bearer token on every request. The four most common patterns are:
  1. Daily sync — Query today’s blocks each morning and push them to a Notion or Obsidian task list to keep your notes in sync with your study plan.
  2. Progress dashboard — Call GET /v1/stats weekly to pull streak counts and completion rates into Grafana, Notion charts, or any BI tool you already use.
  3. Schedule automation — Create blocks programmatically from a course syllabus CSV or calendar .ics file so you never have to enter sessions by hand.
  4. AI assistant — Connect the Take Time MCP Server to Claude or Cursor so your AI assistant can read and update your schedule via natural language.
All API requests require the Authorization: Bearer <key> header. Requests without a valid key return 401 UNAUTHORIZED.

Listing today’s blocks

The GET /v1/blocks endpoint accepts a date query parameter in YYYY-MM-DD format. Fetch today’s blocks and filter for incomplete sessions to build a quick daily briefing.
const today = new Date().toISOString().split('T')[0];

const res = await fetch(`https://api.taketime.app/v1/blocks?date=${today}`, {
  headers: { 'Authorization': 'Bearer tt_live_your_key_here' }
});

const { data, error } = await res.json();

if (error) {
  console.error('API error:', error.code, error.message);
  process.exit(1);
}

const pending = data.filter(b => !b.done);
console.log(`${pending.length} blocks remaining today`);
For multi-day queries, use the date_from and date_to parameters instead of calling the endpoint once per day. A single ranged request is faster and counts as one API call against your rate limit.

Marking a block complete

Send a PATCH request to /v1/blocks/:id with done: true to mark a block complete. You can also update the topic, start, end, or subject_id fields in the same request.
const blockId = 'blk_abc123';

const res = await fetch(`https://api.taketime.app/v1/blocks/${blockId}`, {
  method: 'PATCH',
  headers: {
    'Authorization': 'Bearer tt_live_your_key_here',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ done: true })
});

const { data, error } = await res.json();

if (error) {
  console.error('Failed to mark block done:', error.code, error.message);
} else {
  console.log('Block marked complete:', data.id);
}
Marking a block done: true is reversible — you can PATCH it back to done: false — but doing so may affect your streak calculation if the day has already rolled over.

Pulling progress stats

GET /v1/stats returns aggregated progress data for a given time period. Use the period parameter — today, week, month, or all — to control how far back the data goes.
const res = await fetch('https://api.taketime.app/v1/stats?period=week', {
  headers: { 'Authorization': 'Bearer tt_live_your_key_here' }
});

const { data, error } = await res.json();

if (error) {
  console.error('Stats error:', error.code, error.message);
  process.exit(1);
}

const { completion_rate, streak_days, heatmap } = data;

console.log(`Completion rate this week: ${(completion_rate * 100).toFixed(1)}%`);
console.log(`Current streak: ${streak_days} days`);
// heatmap is an object keyed by date with a session count value
Object.entries(heatmap).forEach(([date, count]) => {
  console.log(`  ${date}: ${count} session(s) completed`);
});
The response data object includes:
FieldTypeDescription
total_blocksintegerTotal blocks in the period.
completed_blocksintegerNumber of blocks marked done.
completion_ratenumberFraction of scheduled blocks completed (0–1).
streak_daysintegerCurrent consecutive-day streak.
total_minutesintegerTotal study minutes in the period.
heatmapobjectMap of { "YYYY-MM-DD": count } for each day in the period.
Valid values for period: today, week, month, all.

Error handling

Every Take Time API response uses a consistent { data, error, meta } envelope. The data field is null when an error occurs, and the error object contains a machine-readable code and a human-readable message.
Node.js
async function apiRequest(url, options = {}) {
  const res = await fetch(url, {
    ...options,
    headers: {
      'Authorization': `Bearer ${process.env.TAKETIME_API_KEY}`,
      'Content-Type': 'application/json',
      ...options.headers
    }
  });

  const body = await res.json();

  if (body.error) {
    const { code, message } = body.error;

    switch (code) {
      case 'VALIDATION_ERROR':
        throw new Error(`Bad request — check your input fields: ${message}`);
      case 'NOT_FOUND':
        throw new Error(`Resource not found: ${message}`);
      case 'RATE_LIMITED': {
        const retryAfter = res.headers.get('Retry-After') ?? 'unknown';
        throw new Error(`Rate limited. Retry after ${retryAfter} seconds.`);
      }
      case 'UNAUTHORIZED':
        throw new Error('Invalid or missing API key.');
      default:
        throw new Error(`API error [${code}]: ${message}`);
    }
  }

  return body.data;
}
Key error codes you’ll encounter:
CodeMeaning
VALIDATION_ERROROne or more input fields failed validation. Check the message for field-level details.
NOT_FOUNDThe block or activity ID you referenced does not exist.
RATE_LIMITEDYou’ve exceeded your plan’s request quota. Respect the Retry-After response header.
UNAUTHORIZEDThe API key is missing, malformed, or revoked.
Never log or expose your full API key in error output or client-side code. If a key is compromised, revoke it immediately in Settings → Developer and generate a replacement.

Best practices

Use tt_test_ prefixed keys during development. Test keys give you full API access in an isolated sandbox so your experiments never touch real study data.
  • Use date range filters. Pass date_from and date_to to GET /v1/blocks instead of making one request per day. Range queries reduce round-trips and are counted as a single API call.
  • Always check error before reading data. Even a 200 OK response can carry an error object in edge cases. Treat error !== null as a failure regardless of HTTP status.
  • Handle RATE_LIMITED gracefully. Read the Retry-After header and implement exponential backoff rather than hammering the API on failure.
  • Validate block IDs before patching. Call GET /v1/blocks with a date filter to confirm a block exists before sending a PATCH. This avoids NOT_FOUND errors in automation scripts.
  • Store your API key in environment variables. Never hardcode keys in source files. Use process.env.TAKETIME_API_KEY in Node.js or your platform’s secrets manager.