API publica para buscar, filtrar, fixar e ordenar repositorios publicos de qualquer usuario do GitHub. Cache inteligente, rate limiting e i18n.
Site • Docs • Playground • Exemplos • LinkedIn
https://api-pearl-nine-29.vercel.app/api/github?user=USERNAME
A GitReposAPI e uma API REST publica e gratuita que funciona como uma camada inteligente sobre a API do GitHub. Em vez de lidar com paginacao, formatacao e filtragem manualmente, voce faz uma unica chamada e recebe dados limpos, organizados e prontos para usar.
Ideal para:
- Secoes de projetos em portfolios pessoais
- Dashboards com estatisticas de GitHub
- Curriculos dinamicos que atualizam sozinhos
- Qualquer aplicacao que precise listar repos de um usuario
Por que usar em vez da API do GitHub direto?
| API do GitHub | GitReposAPI | |
|---|---|---|
| Paginacao | Voce implementa | Automatica |
| Filtragem | Voce implementa | Por query param |
| Ordenacao | Limitada | 7 opcoes + direcao |
| Estatisticas | Nao tem | Agregadas automaticamente |
| Formato | Dados brutos (50+ campos) | Limpo e organizado (campos uteis) |
| CORS | Nao habilitado | Habilitado para qualquer dominio |
curl "https://api-pearl-nine-29.vercel.app/api/github?user=USERNAME"Substitua
USERNAMEpor qualquer username valido do GitHub.
{
"user": "USERNAME",
"cache": { "hit": false, "ttl": 600 },
"pagination": { "page": 1, "total_items": 25, "has_next": true },
"stats": { "total_repos": 25, "total_stars": 142 },
"projects": [
{
"name": "meu-projeto",
"description": "Descricao do repo",
"language": "TypeScript",
"url": "https://github.com/USERNAME/meu-projeto",
"homepage": "https://meusite.com",
"is_pinned": true,
"stats": { "stars": 15, "forks": 3 }
}
]
}const res = await fetch(
"https://api-pearl-nine-29.vercel.app/api/github?user=USERNAME&sort=stars"
);
const data = await res.json();
data.projects.forEach(repo => {
console.log(`${repo.name} - ${repo.stats.stars} stars`);
});Pronto. Sem autenticacao, sem cadastro, sem configuracao.
GET https://api-pearl-nine-29.vercel.app/api/github
Todos via query string (?chave=valor&chave2=valor2):
| Parametro | Tipo | Obrigatorio | Default | Descricao |
|---|---|---|---|---|
user |
string |
Sim | - | Username do GitHub |
language |
string |
Nao | - | Filtra por linguagem (ex: TypeScript, Python) |
topic |
string |
Nao | - | Filtra por topico/tag do repositorio |
search |
string |
Nao | - | Busca no nome ou descricao (max 100 chars) |
sort |
string |
Nao | updated |
Campo de ordenacao (veja abaixo) |
order |
string |
Nao | desc |
Direcao: asc ou desc |
page |
number |
Nao | 1 |
Numero da pagina |
per_page |
number |
Nao | 10 |
Itens por pagina (min: 1, max: 100) |
include_forks |
boolean |
Nao | false |
Incluir repos que sao forks |
include_archived |
boolean |
Nao | false |
Incluir repos arquivados |
stats_only |
boolean |
Nao | false |
Retorna apenas estatisticas |
pinned |
boolean |
Nao | false |
Retorna apenas repos fixados no perfil |
| Valor | Ordena por |
|---|---|
updated |
Data da ultima atualizacao (padrao) |
created |
Data de criacao |
pushed |
Data do ultimo push |
name |
Nome (alfabetico) |
stars |
Numero de stars |
forks |
Numero de forks |
size |
Tamanho do repositorio |
Todos os filtros sao case-insensitive:
typescript,TypeScripteTYPESCRIPTfuncionam igual.
GET /api/github?user=torvalds
GET /api/github?user=torvalds&language=C&sort=stars&order=desc
GET /api/github?user=vercel&search=next
GET /api/github?user=facebook&page=2&per_page=5
GET /api/github?user=torvalds&stats_only=true
Resposta:
{
"user": "torvalds",
"stats": {
"total_repos": 7,
"total_stars": 209847,
"total_forks": 55231,
"languages": [
{ "name": "C", "count": 3 },
{ "name": "Shell", "count": 1 }
],
"topics": []
}
}GET /api/github?user=dev-erickydias&pinned=true
Resposta: apenas os repos que o usuario fixou no perfil do GitHub, com is_pinned: true.
GET /api/github?user=USERNAME&include_forks=true&include_archived=true
GET /api/github?user=USERNAME&language=TypeScript&sort=stars&order=desc&per_page=5&page=1
Repos TypeScript, ordenados por stars (desc), 5 por pagina, primeira pagina.
{
"user": "USERNAME",
"cache": { "hit": false, "ttl": 600 },
"pagination": {
"page": 1,
"per_page": 10,
"total_items": 25,
"total_pages": 3,
"has_next": true,
"has_prev": false
},
"stats": {
"total_repos": 25,
"total_stars": 142,
"total_forks": 38,
"languages": [{ "name": "TypeScript", "count": 12 }],
"topics": [{ "name": "react", "count": 6 }]
},
"projects": [
{
"id": 123456789,
"name": "meu-projeto",
"full_name": "USERNAME/meu-projeto",
"description": "Descricao do repositorio",
"url": "https://github.com/USERNAME/meu-projeto",
"clone_url": "https://github.com/USERNAME/meu-projeto.git",
"homepage": "https://meusite.com",
"language": "TypeScript",
"topics": ["react", "nextjs"],
"default_branch": "main",
"visibility": "public",
"is_fork": false,
"is_archived": false,
"is_template": false,
"is_pinned": true,
"license": { "key": "mit", "name": "MIT License" },
"stats": {
"stars": 15,
"forks": 3,
"watchers": 15,
"open_issues": 2,
"size_kb": 1024
},
"dates": {
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2026-03-28T14:20:00Z",
"pushed_at": "2026-03-28T14:20:00Z",
"days_since_update": 4
},
"has": {
"pages": true,
"wiki": true,
"issues": true,
"projects": true,
"discussions": false
}
}
]
}Informacoes basicas
| Campo | Tipo | Descricao |
|---|---|---|
id |
number |
ID unico no GitHub |
name |
string |
Nome do repositorio |
full_name |
string |
Nome completo (usuario/repo) |
description |
string|null |
Descricao |
url |
string |
URL do repo no GitHub |
clone_url |
string |
URL para clonar |
homepage |
string|null |
URL do site/demo |
language |
string|null |
Linguagem principal |
topics |
string[] |
Tags/topicos |
default_branch |
string |
Branch principal |
visibility |
string |
Visibilidade |
is_fork |
boolean |
Se e fork |
is_archived |
boolean |
Se esta arquivado |
is_template |
boolean |
Se e template |
is_pinned |
boolean |
Se esta fixado no perfil |
license |
object|null |
Licenca (key e name) |
Estatisticas (stats)
| Campo | Tipo | Descricao |
|---|---|---|
stats.stars |
number |
Stars |
stats.forks |
number |
Forks |
stats.watchers |
number |
Watchers |
stats.open_issues |
number |
Issues abertas |
stats.size_kb |
number |
Tamanho em KB |
Datas (dates)
| Campo | Tipo | Descricao |
|---|---|---|
dates.created_at |
ISO 8601 |
Criacao |
dates.updated_at |
ISO 8601 |
Ultima atualizacao |
dates.pushed_at |
ISO 8601 |
Ultimo push |
dates.days_since_update |
number |
Dias desde atualizacao |
Features (has)
| Campo | Tipo | Descricao |
|---|---|---|
has.pages |
boolean |
GitHub Pages ativo |
has.wiki |
boolean |
Wiki habilitada |
has.issues |
boolean |
Issues habilitadas |
has.projects |
boolean |
Projects habilitados |
has.discussions |
boolean |
Discussions habilitadas |
A API sempre retorna JSON com mensagens claras:
| Status | Quando acontece | Exemplo de resposta |
|---|---|---|
400 |
Faltou o user |
{ "error": "Missing required parameter: user", "usage": "...", "params": {...} } |
400 |
Username invalido | { "error": "Invalid GitHub username format" } |
400 |
Sort invalido | { "error": "Invalid sort value. Must be one of: ..." } |
400 |
Order invalido | { "error": "Invalid order value. Must be 'asc' or 'desc'" } |
404 |
Usuario nao existe | { "error": "User \"USERNAME\" not found" } |
429 |
Rate limit estourado | { "error": "GitHub API rate limit exceeded. Try again later." } |
502 |
Erro inesperado | { "error": "Failed to fetch GitHub repositories" } |
JavaScript / Fetch
const API = "https://api-pearl-nine-29.vercel.app/api/github";
// Busca basica
const data = await fetch(`${API}?user=USERNAME`).then(r => r.json());
console.log(data.stats);
console.log(data.projects);
// Com filtros
const filtered = await fetch(
`${API}?user=USERNAME&language=TypeScript&sort=stars`
).then(r => r.json());
// Paginacao
const page2 = await fetch(
`${API}?user=USERNAME&page=2&per_page=5`
).then(r => r.json());
console.log(page2.pagination.has_next); // true/falseReact / Next.js
"use client";
import { useState, useEffect } from "react";
const API = "https://api-pearl-nine-29.vercel.app/api/github";
export default function Projects() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`${API}?user=USERNAME&sort=stars&per_page=6`)
.then(res => res.json())
.then(setData)
.finally(() => setLoading(false));
}, []);
if (loading) return <p>Carregando...</p>;
if (!data) return <p>Erro ao carregar</p>;
return (
<section>
{/* Stats */}
<div>
<span>{data.stats.total_repos} repos</span>
<span>{data.stats.total_stars} stars</span>
</div>
{/* Projects */}
{data.projects.map(repo => (
<div key={repo.id}>
<h3>
<a href={repo.url} target="_blank" rel="noopener noreferrer">
{repo.name}
</a>
</h3>
<p>{repo.description || "Sem descricao"}</p>
<span>{repo.language}</span>
<span>{repo.stats.stars} stars</span>
{repo.homepage && (
<a href={repo.homepage} target="_blank" rel="noopener noreferrer">
Demo
</a>
)}
</div>
))}
</section>
);
}Python
import requests
API = "https://api-pearl-nine-29.vercel.app/api/github"
# Buscar repos
data = requests.get(API, params={
"user": "USERNAME",
"sort": "stars",
"per_page": 20,
}).json()
# Stats
print(f"Total: {data['stats']['total_repos']} repos")
print(f"Stars: {data['stats']['total_stars']}")
# Projetos
for repo in data["projects"]:
print(f" {repo['name']} ({repo['language']}) - {repo['stats']['stars']} stars")
# Apenas stats
stats = requests.get(API, params={
"user": "USERNAME",
"stats_only": "true"
}).json()cURL
# Busca basica
curl "https://api-pearl-nine-29.vercel.app/api/github?user=USERNAME"
# Com filtros
curl "https://api-pearl-nine-29.vercel.app/api/github?user=USERNAME&language=TypeScript&sort=stars"
# Apenas stats
curl "https://api-pearl-nine-29.vercel.app/api/github?user=USERNAME&stats_only=true"
# Formatado com jq
curl -s "https://api-pearl-nine-29.vercel.app/api/github?user=USERNAME" | jq '.projects[] | {name, stars: .stats.stars}'Seu App GitReposAPI v2 (Vercel) GitHub API
| | |
| GET ?user=USERNAME | |
|---------------------------->| |
| | Rate limit check (30/min) |
| | Valida inputs |
| | (regex, sort, order) |
| | |
| | Cache hit? (TTL 10min) |
| | [sim] -> usa cache |
| | [nao] -> busca no GitHub |
| | |
| | GET /users/USERNAME/repos |
| |----------------------------->|
| | + GraphQL pinnedItems |
| |<-----------------------------|
| | |
| | Filtra (language, topic, |
| | search, forks, pinned) |
| | |
| | Calcula stats |
| | Ordena (sort + order) |
| | Pagina (page + per_page) |
| | |
| JSON + cache info + CORS | |
|<----------------------------| |
- Node.js 18+
- (Opcional) GitHub Token para rate limit de 5k/h
# 1. Clone o repositorio
git clone https://github.com/dev-erickydias/github-api.git
cd github-api
# 2. Instale as dependencias
npm install
# 3. Configure o token (opcional mas recomendado)
cp .env.example .env.local
# Edite .env.local e cole seu GitHub Token
# 4. Rode o servidor
npm run dev
# 5. Acesse
# http://localhost:3000/api/github?user=USERNAME- Acesse github.com/settings/tokens
- Clique em Generate new token > Fine-grained token
- De um nome (ex:
github-api-local) - Para repos publicos, nenhum scope e necessario
- Clique em Generate token e copie
- Cole no
.env.local:GITHUB_TOKEN=seu_token_aqui
| Sem token | Com token | |
|---|---|---|
| Rate limit | 60 req/hora | 5.000 req/hora |
- Faca um fork deste repositorio
- Acesse vercel.com > Add New Project
- Importe o fork
- Em Environment Variables, adicione:
- Name:
GITHUB_TOKEN - Value:
seu_token_github
- Name:
- Clique em Deploy
npm i -g vercel
vercel login
vercel --prod
echo "seu_token" | vercel env add GITHUB_TOKEN productionCada push no repositorio faz redeploy automatico.
| Check | Status | Descricao |
|---|---|---|
| Validacao de username | ✅ | Regex rigorosa, max 39 chars |
| URL encoding | ✅ | encodeURIComponent em todos os params |
| Validacao de sort/order | ✅ | Whitelist de valores permitidos |
| Limite de busca | ✅ | Campo search limitado a 100 chars |
| Anti-loop | ✅ | Max 10 paginas internas (1.000 repos) |
| Rate limit handling | ✅ | 403 do GitHub retornado como 429 |
| Validacao de resposta | ✅ | Verifica Array.isArray antes de processar |
| CORS controlado | ✅ | Apenas GET e OPTIONS permitidos |
| Token protegido | ✅ | Apenas em env vars, nunca exposto |
| Sem dados sensiveis | ✅ | Apenas dados publicos retornados |
| Rate limiting | ✅ | 30 req/min por IP |
| Security headers | ✅ | X-Frame-Options, X-Content-Type, Referrer-Policy |
| Cache bounded | ✅ | Max 100 entradas em memoria com TTL de 10min |
| GraphQL parametrizado | ✅ | Variaveis parametrizadas contra injection |
Auditoria completa: api-pearl-nine-29.vercel.app/security
| Recurso | Limite |
|---|---|
| Rate limit da API | 30 req/min por IP |
| Rate limit GitHub sem token | 60 req/hora |
| Rate limit GitHub com token | 5.000 req/hora |
| Max repos por usuario | 1.000 (10 pag x 100) |
| Max itens por pagina | 100 |
| Max chars no search | 100 |
| Cache TTL | 10 minutos |
| Cache max entries | 100 usuarios |
| Max pinned repos | 6 (limite do GitHub) |
| URL | |
|---|---|
| Site | api-pearl-nine-29.vercel.app |
| Docs | /docs |
| Playground | /playground |
| Exemplos | /examples |
| Repositorio | github.com/dev-erickydias/github-api |
| linkedin.com/in/erickydias |
Feito por Erick Dias
© 2026 Erick Dias. Todos os direitos reservados.