# Github API

> Octokit (official GitHub SDK) best practices, common operations, and code examples for GitHub API automation. Use when creating repos, updating files, managing issues/PRs, or any GitHub API interaction. Use when this capability is needed.

- Skill: `tomevault-io/github-api-2` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/github-api-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/github-api-2/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/github-api-2

---


# GitHub API / Octokit Skill

This skill provides guidance, patterns, and examples for using Octokit (the official GitHub SDK) to interact with the GitHub API.

## When to Use This Skill

Use this skill when you need to:
- Create, update, or manage GitHub repositories
- Update files in repositories programmatically
- Manage issues, pull requests, or discussions
- Work with GitHub Actions, releases, or webhooks
- Automate GitHub operations via the API
- Implement GitHub integrations or bots

## Authentication

### Environment Variable (Recommended)

```javascript
const { Octokit } = require('@octokit/rest');

const octokit = new Octokit({
  auth: process.env.GITHUB_TOKEN
});
```

### Personal Access Token Requirements

Generate at: https://github.com/settings/tokens

**Minimum scopes needed:**
- `repo` - Full control of private repositories (includes public repos)
- `workflow` - Update GitHub Action workflows (if needed)
- `admin:org` - Manage organization settings (if working with orgs)

**Token format:** Starts with `ghp_` (personal access token)

### Checking Authentication

```javascript
// Test if token is valid
const { data: user } = await octokit.users.getAuthenticated();
console.log(`Authenticated as: ${user.login}`);
```

## Common Operations

### 1. Repository Operations

#### Create Repository

```javascript
const { data: repo } = await octokit.repos.createForAuthenticatedUser({
  name: 'my-new-repo',
  description: 'Repository description',
  private: false,
  auto_init: true, // Initialize with README
  license_template: 'mit', // Optional: add license
});

console.log(`Created: ${repo.html_url}`);
```

#### Get Repository Info

```javascript
const { data: repo } = await octokit.repos.get({
  owner: 'username',
  repo: 'repo-name'
});

console.log(`Stars: ${repo.stargazers_count}`);
console.log(`Forks: ${repo.forks_count}`);
console.log(`Default branch: ${repo.default_branch}`);
```

#### Update Repository Settings

```javascript
await octokit.repos.update({
  owner: 'username',
  repo: 'repo-name',
  description: 'Updated description',
  homepage: 'https://example.com',
  has_issues: true,
  has_wiki: false,
  has_projects: true,
});
```

#### Delete Repository

```javascript
await octokit.repos.delete({
  owner: 'username',
  repo: 'repo-name'
});
```

### 2. File Operations

#### Get File Contents

```javascript
const { data: file } = await octokit.repos.getContent({
  owner: 'username',
  repo: 'repo-name',
  path: 'path/to/file.md',
  ref: 'main' // branch name
});

// Decode base64 content
const content = Buffer.from(file.content, 'base64').toString('utf8');
console.log(content);
```

#### Create or Update File

```javascript
const { data } = await octokit.repos.createOrUpdateFileContents({
  owner: 'username',
  repo: 'repo-name',
  path: 'path/to/file.md',
  message: 'Commit message',
  content: Buffer.from('file content here').toString('base64'),
  sha: file.sha, // Required for updates, omit for new files
  branch: 'main'
});

console.log(`Commit: ${data.commit.html_url}`);
```

**Important:** Content must be base64 encoded!

#### Delete File

```javascript
await octokit.repos.deleteFile({
  owner: 'username',
  repo: 'repo-name',
  path: 'path/to/file.md',
  message: 'Delete file',
  sha: file.sha, // Required - get from getContent first
  branch: 'main'
});
```

#### Get Multiple Files (Directory)

```javascript
const { data: contents } = await octokit.repos.getContent({
  owner: 'username',
  repo: 'repo-name',
  path: 'src',
  ref: 'main'
});

// contents is an array if path is a directory
contents.forEach(item => {
  console.log(`${item.type}: ${item.path}`);
});
```

### 3. Branch Operations

#### List Branches

```javascript
const { data: branches } = await octokit.repos.listBranches({
  owner: 'username',
  repo: 'repo-name'
});

branches.forEach(branch => {
  console.log(`${branch.name}: ${branch.commit.sha}`);
});
```

#### Create Branch

```javascript
// First, get the SHA of the commit you want to branch from
const { data: ref } = await octokit.git.getRef({
  owner: 'username',
  repo: 'repo-name',
  ref: 'heads/main'
});

// Create new branch
await octokit.git.createRef({
  owner: 'username',
  repo: 'repo-name',
  ref: 'refs/heads/new-branch-name',
  sha: ref.object.sha
});
```

#### Delete Branch

```javascript
await octokit.git.deleteRef({
  owner: 'username',
  repo: 'repo-name',
  ref: 'heads/branch-to-delete'
});
```

### 4. Issues and Pull Requests

#### Create Issue

```javascript
const { data: issue } = await octokit.issues.create({
  owner: 'username',
  repo: 'repo-name',
  title: 'Issue title',
  body: 'Issue description',
  labels: ['bug', 'help wanted'],
  assignees: ['username']
});

console.log(`Issue #${issue.number}: ${issue.html_url}`);
```

#### Update Issue

```javascript
await octokit.issues.update({
  owner: 'username',
  repo: 'repo-name',
  issue_number: 123,
  state: 'closed',
  body: 'Updated description'
});
```

#### List Issues

```javascript
const { data: issues } = await octokit.issues.listForRepo({
  owner: 'username',
  repo: 'repo-name',
  state: 'open', // 'open', 'closed', 'all'
  labels: 'bug',
  sort: 'created',
  direction: 'desc',
  per_page: 100
});
```

#### Create Pull Request

```javascript
const { data: pr } = await octokit.pulls.create({
  owner: 'username',
  repo: 'repo-name',
  title: 'PR title',
  body: 'PR description',
  head: 'feature-branch', // branch with changes
  base: 'main' // target branch
});

console.log(`PR #${pr.number}: ${pr.html_url}`);
```

### 5. Releases

#### Create Release

```javascript
const { data: release } = await octokit.repos.createRelease({
  owner: 'username',
  repo: 'repo-name',
  tag_name: 'v1.0.0',
  name: 'Version 1.0.0',
  body: 'Release notes here',
  draft: false,
  prerelease: false
});
```

#### List Releases

```javascript
const { data: releases } = await octokit.repos.listReleases({
  owner: 'username',
  repo: 'repo-name'
});
```

### 6. Organization Operations

#### List Repositories

```javascript
const { data: repos } = await octokit.repos.listForOrg({
  org: 'organization-name',
  type: 'all', // 'all', 'public', 'private'
  sort: 'updated',
  per_page: 100
});
```

#### Create Organization Repository

```javascript
const { data: repo } = await octokit.repos.createInOrg({
  org: 'organization-name',
  name: 'repo-name',
  description: 'Description',
  private: false
});
```

### 7. User Operations

#### Get Authenticated User

```javascript
const { data: user } = await octokit.users.getAuthenticated();
console.log(`Username: ${user.login}`);
console.log(`Name: ${user.name}`);
console.log(`Email: ${user.email}`);
```

#### List User Repositories

```javascript
const { data: repos } = await octokit.repos.listForAuthenticatedUser({
  visibility: 'all', // 'all', 'public', 'private'
  sort: 'updated',
  per_page: 100
});
```

## Pagination

For endpoints that return many results, use pagination:

```javascript
const iterator = octokit.paginate.iterator(octokit.repos.listForAuthenticatedUser, {
  per_page: 100
});

for await (const { data: repos } of iterator) {
  for (const repo of repos) {
    console.log(repo.name);
  }
}
```

Or get all at once:

```javascript
const allRepos = await octokit.paginate(octokit.repos.listForAuthenticatedUser);
console.log(`Total repos: ${allRepos.length}`);
```

## Error Handling

### Basic Error Handling

```javascript
try {
  const { data } = await octokit.repos.get({
    owner: 'username',
    repo: 'repo-name'
  });
} catch (error) {
  console.error('Error:', error.message);
  
  if (error.status === 404) {
    console.error('Repository not found');
  } else if (error.status === 401) {
    console.error('Authentication failed - check your token');
  } else if (error.status === 403) {
    console.error('Forbidden - insufficient permissions');
  }
  
  if (error.response) {
    console.error('Response:', error.response.data);
  }
}
```

### Common Error Codes

- `401` - Bad credentials / invalid token
- `403` - Forbidden / rate limited / insufficient permissions
- `404` - Not found
- `422` - Validation failed / unprocessable entity
- `500` - Server error

### Rate Limiting

Check rate limit status:

```javascript
const { data: rateLimit } = await octokit.rateLimit.get();
console.log(`Remaining: ${rateLimit.rate.remaining}/${rateLimit.rate.limit}`);
console.log(`Reset: ${new Date(rateLimit.rate.reset * 1000)}`);
```

## Best Practices

### 1. Always Check Authentication First

```javascript
async function verifyAuth(octokit) {
  try {
    const { data: user } = await octokit.users.getAuthenticated();
    console.log(`✅ Authenticated as: ${user.login}`);
    return true;
  } catch (error) {
    console.error('❌ Authentication failed:', error.message);
    return false;
  }
}
```

### 2. Get SHA Before Updating Files

```javascript
async function updateFile(owner, repo, path, content, message) {
  // Get current file to get SHA
  const { data: currentFile } = await octokit.repos.getContent({
    owner,
    repo,
    path
  });
  
  // Update with SHA
  await octokit.repos.createOrUpdateFileContents({
    owner,
    repo,
    path,
    message,
    content: Buffer.from(content).toString('base64'),
    sha: currentFile.sha
  });
}
```

### 3. Use Environment Variables for Tokens

**Never hardcode tokens!**

```javascript
// ✅ Good
const octokit = new Octokit({
  auth: process.env.GITHUB_TOKEN
});

// ❌ Bad
const octokit = new Octokit({
  auth: 'ghp_hardcoded_token_here' // NEVER DO THIS!
});
```

### 4. Handle Pagination for Large Result Sets

```javascript
async function getAllRepos(octokit) {
  return await octokit.paginate(octokit.repos.listForAuthenticatedUser, {
    per_page: 100
  });
}
```

### 5. Batch Operations with Error Recovery

```javascript
async function updateMultipleFiles(files) {
  const results = [];
  
  for (const file of files) {
    try {
      const result = await updateFile(file.path, file.content);
      results.push({ success: true, file: file.path, result });
    } catch (error) {
      results.push({ success: false, file: file.path, error: error.message });
    }
  }
  
  return results;
}
```

## Complete Example Script

```javascript
#!/usr/bin/env node

const { Octokit } = require('@octokit/rest');
const fs = require('fs');

// Initialize Octokit
const octokit = new Octokit({
  auth: process.env.GITHUB_TOKEN
});

const owner = 'username';
const repo = 'repo-name';
const filePath = 'README.md';

async function main() {
  try {
    // 1. Verify authentication
    const { data: user } = await octokit.users.getAuthenticated();
    console.log(`✅ Authenticated as: ${user.login}`);
    
    // 2. Get current file
    console.log(`\n📥 Getting ${filePath}...`);
    const { data: currentFile } = await octokit.repos.getContent({
      owner,
      repo,
      path: filePath
    });
    console.log(`Current SHA: ${currentFile.sha}`);
    
    // 3. Read new content from local file
    const newContent = fs.readFileSync('./new-readme.md', 'utf8');
    
    // 4. Update file
    console.log(`\n📤 Updating ${filePath}...`);
    const { data } = await octokit.repos.createOrUpdateFileContents({
      owner,
      repo,
      path: filePath,
      message: 'Update README with new content',
      content: Buffer.from(newContent).toString('base64'),
      sha: currentFile.sha
    });
    
    console.log(`\n✅ Successfully updated ${filePath}`);
    console.log(`Commit: ${data.commit.html_url}`);
    
  } catch (error) {
    console.error('\n❌ Error:', error.message);
    if (error.response) {
      console.error('Response:', error.response.data);
    }
    process.exit(1);
  }
}

main();
```

## Installation

```bash
npm install @octokit/rest
# or
yarn add @octokit/rest
```

## TypeScript Support

Octokit has full TypeScript support:

```typescript
import { Octokit } from '@octokit/rest';

const octokit = new Octokit({
  auth: process.env.GITHUB_TOKEN
});

// Type-safe API calls
const { data: repo } = await octokit.repos.get({
  owner: 'username',
  repo: 'repo-name'
});

// repo is fully typed
console.log(repo.stargazers_count);
```

## Useful Links

- **Octokit Documentation:** https://octokit.github.io/rest.js/
- **GitHub REST API Docs:** https://docs.github.com/en/rest
- **Generate Token:** https://github.com/settings/tokens
- **Rate Limits:** https://docs.github.com/en/rest/rate-limit
- **Octokit GitHub:** https://github.com/octokit/rest.js

## Common Patterns

### Pattern: Update File in Repository

```javascript
async function updateRepoFile(owner, repo, path, content, message) {
  const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
  
  // Get current file for SHA
  const { data: currentFile } = await octokit.repos.getContent({
    owner,
    repo,
    path
  });
  
  // Update
  const { data } = await octokit.repos.createOrUpdateFileContents({
    owner,
    repo,
    path,
    message,
    content: Buffer.from(content).toString('base64'),
    sha: currentFile.sha
  });
  
  return data.commit.html_url;
}
```

### Pattern: Clone Repository Settings

```javascript
async function cloneSettings(sourceRepo, targetRepo) {
  const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
  
  // Get source repo settings
  const { data: source } = await octokit.repos.get({
    owner: 'username',
    repo: sourceRepo
  });
  
  // Apply to target
  await octokit.repos.update({
    owner: 'username',
    repo: targetRepo,
    description: source.description,
    homepage: source.homepage,
    has_issues: source.has_issues,
    has_wiki: source.has_wiki,
    has_projects: source.has_projects
  });
}
```

### Pattern: Bulk File Operations

```javascript
async function updateMultipleFiles(owner, repo, files) {
  const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
  const results = [];
  
  for (const file of files) {
    try {
      const url = await updateRepoFile(
        owner,
        repo,
        file.path,
        file.content,
        file.message
      );
      results.push({ success: true, path: file.path, url });
    } catch (error) {
      results.push({ success: false, path: file.path, error: error.message });
    }
  }
  
  return results;
}
```

## Troubleshooting

### "Bad credentials" Error

- Check that GITHUB_TOKEN is set: `echo $GITHUB_TOKEN`
- Verify token starts with `ghp_`
- Generate new token at: https://github.com/settings/tokens
- Ensure token has correct scopes (usually `repo`)

### "Not Found" Error (404)

- Verify repository exists
- Check spelling of owner/repo names
- Ensure token has access to private repos (if applicable)
- Check if repository is in an organization you don't have access to

### "Validation Failed" Error (422)

- For file updates: Ensure SHA is provided and correct
- For branch creation: Check branch name doesn't already exist
- For PRs: Verify head and base branches exist

### Rate Limiting (403)

- Check rate limit: `octokit.rateLimit.get()`
- Authenticated requests: 5,000/hour
- Unauthenticated: 60/hour
- Wait for reset time or use multiple tokens

## Resources

- [github-manager.js](../../../github-manager.js) - Full implementation example
- [Octokit REST API Docs](https://octokit.github.io/rest.js/)
- [GitHub REST API Reference](https://docs.github.com/en/rest)

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/jchidley) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-14 -->

