Skip to main content
This page provides complete, working examples of common REST API operations in Sakai.

Prerequisites

All examples assume:
  • You have a running Sakai instance at https://sakai.example.edu
  • You have valid user credentials
  • HTTPS is enabled (required for production)

Basic Authentication Example

Login and Get User Announcements

# 1. Login and save session cookie
curl -X POST https://sakai.example.edu/api/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "[email protected]&password=your-password" \
  -c cookies.txt

# 2. Get announcements using the session cookie
curl https://sakai.example.edu/api/users/me/announcements \
  -b cookies.txt

# 3. Logout when done
curl -X POST https://sakai.example.edu/api/logout \
  -b cookies.txt

Dashboard Data Example

Get a complete dashboard view with announcements, calendar events, and grades:
# Login first
curl -X POST https://sakai.example.edu/api/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "[email protected]&password=your-password" \
  -c cookies.txt

# Get dashboard data
curl https://sakai.example.edu/api/users/me/dashboard \
  -b cookies.txt \
  | jq '.'

Calendar Events Example

Retrieve and display upcoming calendar events:
async function getUpcomingEvents() {
  // Assuming already logged in
  
  const response = await fetch(
    'https://sakai.example.edu/api/users/me/calendar',
    { credentials: 'include' }
  );

  const data = await response.json();
  const events = data.events;

  // Filter events happening in the next 7 days
  const now = new Date();
  const weekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);

  const upcomingEvents = events.filter(event => {
    const eventDate = new Date(event.startTime);
    return eventDate >= now && eventDate <= weekFromNow;
  });

  // Display events
  upcomingEvents.forEach(event => {
    console.log(`${event.title} - ${new Date(event.startTime).toLocaleString()}`);
  });

  return upcomingEvents;
}

Site Grades Example

Get gradebook information for a specific site:
def get_site_grades(session, site_id):
    """Get grades for a specific site"""
    
    response = session.get(
        f'https://sakai.example.edu/api/sites/{site_id}/grades'
    )
    
    if response.ok:
        grades = response.json()
        
        # Display grade items
        for item in grades.get('items', []):
            print(f"{item['name']}: {item['score']}/{item['points']}")
            
        # Display course grade
        course_grade = grades.get('courseGrade')
        if course_grade:
            print(f"\nCourse Grade: {course_grade['displayGrade']}")
            
        return grades
    else:
        print(f"Error: {response.status_code}")
        return None

# Usage
session = create_authenticated_session()
grades = get_site_grades(session, 'chemistry-101-site-id')

Error Handling Example

Robust error handling for REST API calls:
class SakaiAPIClient {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
    this.isAuthenticated = false;
  }

  async login(username, password) {
    try {
      const response = await fetch(`${this.baseUrl}/api/login`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({ username, password }),
        credentials: 'include'
      });

      if (!response.ok) {
        if (response.status === 401) {
          throw new Error('Invalid credentials');
        }
        throw new Error(`Login failed: ${response.statusText}`);
      }

      this.isAuthenticated = true;
      const data = await response.json();
      return data;

    } catch (error) {
      console.error('Login error:', error);
      this.isAuthenticated = false;
      throw error;
    }
  }

  async makeRequest(endpoint, options = {}) {
    if (!this.isAuthenticated) {
      throw new Error('Not authenticated. Call login() first.');
    }

    try {
      const response = await fetch(`${this.baseUrl}${endpoint}`, {
        ...options,
        credentials: 'include'
      });

      // Handle session timeout
      if (response.status === 401) {
        this.isAuthenticated = false;
        throw new Error('Session expired. Please login again.');
      }

      // Handle permission errors
      if (response.status === 403) {
        throw new Error('Permission denied');
      }

      // Handle not found
      if (response.status === 404) {
        throw new Error('Resource not found');
      }

      // Handle server errors
      if (response.status >= 500) {
        throw new Error('Server error. Please try again later.');
      }

      if (!response.ok) {
        throw new Error(`Request failed: ${response.statusText}`);
      }

      return await response.json();

    } catch (error) {
      console.error(`API request error (${endpoint}):`, error);
      throw error;
    }
  }

  async getAnnouncements() {
    return this.makeRequest('/api/users/me/announcements');
  }

  async getCalendar() {
    return this.makeRequest('/api/users/me/calendar');
  }

  async logout() {
    try {
      await this.makeRequest('/api/logout', { method: 'POST' });
      this.isAuthenticated = false;
    } catch (error) {
      console.error('Logout error:', error);
    }
  }
}

// Usage
const client = new SakaiAPIClient('https://sakai.example.edu');

async function example() {
  try {
    await client.login('[email protected]', 'password');
    const announcements = await client.getAnnouncements();
    console.log(announcements);
  } catch (error) {
    console.error('Error:', error.message);
  } finally {
    await client.logout();
  }
}

Reusable Python Client

A complete Python client with session management:
import requests
from typing import Optional, Dict, Any

class SakaiClient:
    """Reusable Sakai REST API client"""
    
    def __init__(self, base_url: str):
        self.base_url = base_url.rstrip('/')
        self.session = requests.Session()
        self.is_authenticated = False
    
    def login(self, username: str, password: str) -> Dict[str, Any]:
        """Login and establish a session"""
        response = self.session.post(
            f'{self.base_url}/api/login',
            data={'username': username, 'password': password}
        )
        
        if response.ok:
            self.is_authenticated = True
            return response.json()
        else:
            raise Exception(f"Login failed: {response.status_code}")
    
    def _request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
        """Make an authenticated request"""
        if not self.is_authenticated:
            raise Exception("Not authenticated. Call login() first.")
        
        url = f'{self.base_url}{endpoint}'
        response = self.session.request(method, url, **kwargs)
        
        # Handle session timeout
        if response.status_code == 401:
            self.is_authenticated = False
            raise Exception("Session expired")
        
        response.raise_for_status()
        return response
    
    def get_announcements(self) -> Dict[str, Any]:
        """Get user announcements"""
        response = self._request('GET', '/api/users/me/announcements')
        return response.json()
    
    def get_calendar(self) -> Dict[str, Any]:
        """Get user calendar events"""
        response = self._request('GET', '/api/users/me/calendar')
        return response.json()
    
    def get_site_grades(self, site_id: str) -> Dict[str, Any]:
        """Get grades for a site"""
        response = self._request('GET', f'/api/sites/{site_id}/grades')
        return response.json()
    
    def logout(self):
        """Logout and end session"""
        if self.is_authenticated:
            self.session.post(f'{self.base_url}/api/logout')
            self.is_authenticated = False

# Usage
if __name__ == '__main__':
    client = SakaiClient('https://sakai.example.edu')
    
    try:
        # Login
        user_info = client.login('[email protected]', 'password')
        print(f"Logged in as: {user_info['displayName']}")
        
        # Get announcements
        announcements = client.get_announcements()
        print(f"Found {len(announcements['announcements'])} announcements")
        
        # Get calendar
        calendar = client.get_calendar()
        print(f"Found {len(calendar['events'])} events")
        
    except Exception as e:
        print(f"Error: {e}")
    finally:
        client.logout()

Best Practices

Session Management

  • Reuse sessions for multiple requests
  • Handle session timeouts gracefully
  • Always logout when done

Error Handling

  • Check HTTP status codes
  • Handle 401 (unauthorized) and 403 (forbidden) errors
  • Implement retry logic for temporary failures

Security

  • Always use HTTPS in production
  • Never log or expose credentials
  • Store credentials securely

Performance

  • Reuse HTTP connections/sessions
  • Cache responses when appropriate
  • Use pagination for large datasets

Next Steps

API Endpoints

Explore all available REST endpoints

Authentication

Learn more about authentication methods

Build docs developers (and LLMs) love