Back

Step by Step Guide to Building a Workflowy API Integration in Python

Aug 16, 20247 minute read

Hey there, fellow developer! Ready to dive into the world of Workflowy API integration? Let's roll up our sleeves and get coding!

Introduction

Workflowy's API is a powerful tool that lets you programmatically interact with your outlines. Whether you're looking to automate your workflow or build a custom integration, this guide will walk you through the process. We'll be using Python, so make sure you've got your favorite IDE fired up!

Prerequisites

Before we jump in, make sure you've got:

  • Python 3.x installed
  • requests library (pip install requests)
  • Your Workflowy API credentials (client ID and secret)

If you're missing any of these, take a quick detour to get set up. Don't worry, we'll wait!

Setting up the API Connection

First things first, let's authenticate:

import requests CLIENT_ID = 'your_client_id' CLIENT_SECRET = 'your_client_secret' auth_url = 'https://workflowy.com/api/auth' response = requests.post(auth_url, json={ 'clientId': CLIENT_ID, 'clientSecret': CLIENT_SECRET }) access_token = response.json()['access_token']

Great! Now we've got our access token. Let's set up our base URL:

BASE_URL = 'https://workflowy.com/api/v1' headers = {'Authorization': f'Bearer {access_token}'}

Basic API Operations

Now for the fun part! Let's start with some basic operations:

Fetching the Outline

response = requests.get(f'{BASE_URL}/outline', headers=headers) outline = response.json()

Creating New Nodes

new_node = { 'name': 'My new node', 'parentId': 'some_parent_id' } response = requests.post(f'{BASE_URL}/nodes', json=new_node, headers=headers)

Updating Existing Nodes

updated_node = { 'id': 'node_id_to_update', 'name': 'Updated node name' } response = requests.put(f'{BASE_URL}/nodes/{updated_node["id"]}', json=updated_node, headers=headers)

Deleting Nodes

node_id_to_delete = 'some_node_id' response = requests.delete(f'{BASE_URL}/nodes/{node_id_to_delete}', headers=headers)

Advanced Features

Ready to level up? Let's tackle some more complex operations:

Working with Tags

def add_tag(node_id, tag): node = requests.get(f'{BASE_URL}/nodes/{node_id}', headers=headers).json() node['name'] += f' #{tag}' requests.put(f'{BASE_URL}/nodes/{node_id}', json=node, headers=headers)

Moving Nodes

def move_node(node_id, new_parent_id): node = {'id': node_id, 'parentId': new_parent_id} requests.put(f'{BASE_URL}/nodes/{node_id}', json=node, headers=headers)

Bulk Operations

def bulk_create_nodes(parent_id, node_names): nodes = [{'name': name, 'parentId': parent_id} for name in node_names] requests.post(f'{BASE_URL}/nodes/bulk', json={'nodes': nodes}, headers=headers)

Error Handling and Best Practices

Always be prepared for things to go wrong:

def api_request(method, endpoint, **kwargs): try: response = requests.request(method, f'{BASE_URL}/{endpoint}', headers=headers, **kwargs) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"API request failed: {e}") return None

Don't forget about rate limiting! Be kind to the API:

import time def rate_limited_request(*args, **kwargs): response = api_request(*args, **kwargs) if response and 'X-RateLimit-Remaining' in response.headers: if int(response.headers['X-RateLimit-Remaining']) < 10: time.sleep(1) return response

Example Use Case: CLI Tool

Let's put it all together with a simple CLI tool:

import argparse def main(): parser = argparse.ArgumentParser(description='Workflowy CLI') parser.add_argument('action', choices=['add', 'update', 'delete']) parser.add_argument('node_id', nargs='?') parser.add_argument('--name', '-n') args = parser.parse_args() if args.action == 'add': new_node = {'name': args.name, 'parentId': args.node_id or 'root'} response = api_request('POST', 'nodes', json=new_node) print(f"Node created: {response['id']}") elif args.action == 'update': updated_node = {'id': args.node_id, 'name': args.name} api_request('PUT', f'nodes/{args.node_id}', json=updated_node) print(f"Node updated: {args.node_id}") elif args.action == 'delete': api_request('DELETE', f'nodes/{args.node_id}') print(f"Node deleted: {args.node_id}") if __name__ == '__main__': main()

Testing and Debugging

Don't forget to test your code! Here's a quick example using unittest and unittest.mock:

import unittest from unittest.mock import patch from your_module import api_request class TestWorkflowyAPI(unittest.TestCase): @patch('your_module.requests.request') def test_api_request(self, mock_request): mock_request.return_value.json.return_value = {'id': '123'} result = api_request('GET', 'nodes/123') self.assertEqual(result, {'id': '123'}) mock_request.assert_called_once() if __name__ == '__main__': unittest.main()

Conclusion

And there you have it! You've just built a Workflowy API integration in Python. From basic operations to advanced features, you're now equipped to create some seriously cool automations and tools.

Remember, the Workflowy API is your playground. Don't be afraid to experiment and push the boundaries of what's possible. Happy coding, and may your outlines always be organized!

For more details, check out the official Workflowy API documentation. Now go forth and build something awesome!