middleburycampus.com APIwww.middleburycampus.com ↗
Access articles, full text, authors, and categories from The Middlebury Campus student newspaper via 3 structured endpoints.
curl -X GET 'https://api.parse.bot/scraper/54e52fbb-5d0c-426e-be2c-2a020b66fad2/search_articles?page=1&query=climate&per_page=20' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed Python client. Install the CLI, sign in, then pull this API’s generated client:
pip install parse-sdk parse login parse add --marketplace middleburycampus-com-api
parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""
Middlebury Campus News API Client
Get your API key from: https://parse.bot/settings
"""
import os
import requests
from typing import Optional, Dict, Any, List
class ParseClient:
"""Client for Middlebury Campus News API"""
def __init__(self, api_key: Optional[str] = None):
self.base_url = "https://api.parse.bot"
self.scraper_id = "54e52fbb-5d0c-426e-be2c-2a020b66fad2"
self.api_key = api_key or os.getenv("PARSE_API_KEY")
if not self.api_key:
raise ValueError("API key required. Set PARSE_API_KEY env var or pass api_key parameter.")
def _call(self, endpoint: str, method: str = "POST", **params) -> Dict[str, Any]:
"""Make API call to Parse endpoint"""
url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
headers = {
"X-API-Key": self.api_key,
"Content-Type": "application/json"
}
if method == "GET":
response = requests.get(url, headers=headers, params=params)
elif method == "POST":
response = requests.post(url, headers=headers, json=params)
else:
raise ValueError(f"Unsupported method: {method}")
response.raise_for_status()
return response.json()
def search_articles(self, query: str, page: int = 1, per_page: int = 20) -> Dict[str, Any]:
"""
Search articles across The Middlebury Campus archives.
Args:
query: Search keywords to match against article content and titles
page: Page number for pagination (default: 1)
per_page: Number of results per page (default: 20)
Returns:
Dictionary with search results, total count, and articles
"""
return self._call(
"search_articles",
method="GET",
query=query,
page=page,
per_page=per_page
)
def get_article(self, article_slug: str) -> Dict[str, Any]:
"""
Retrieve full content of a single article.
Args:
article_slug: Article URL slug in format YYYY/MM/article-title-slug
Returns:
Dictionary with article title, content, authors, date, and metadata
"""
return self._call(
"get_article",
method="GET",
article_slug=article_slug
)
def list_section_articles(
self,
section: str = "news",
page: int = 1,
per_page: int = 20
) -> Dict[str, Any]:
"""
List articles from a specific section.
Args:
section: Section slug (news, local, arts-culture, opinion, sports)
page: Page number for pagination (default: 1)
per_page: Number of results per page (default: 20)
Returns:
Dictionary with paginated articles from the section
"""
return self._call(
"list_section_articles",
method="GET",
section=section,
page=page,
per_page=per_page
)
def main():
"""Practical workflow: search for articles, then fetch full content of top results"""
client = ParseClient()
# Step 1: Search for articles about climate change
print("=" * 70)
print("SEARCHING FOR CLIMATE-RELATED ARTICLES")
print("=" * 70)
search_results = client.search_articles(query="climate", page=1, per_page=5)
if search_results.get("status") == "success":
data = search_results.get("data", {})
total = data.get("total", 0)
articles = data.get("articles", [])
print(f"\nFound {total} articles matching 'climate'")
print(f"Showing page 1 with {len(articles)} results:\n")
for idx, article in enumerate(articles, 1):
print(f"{idx}. {article.get('title')}")
print(f" Date: {article.get('date')}")
print(f" Snippet: {article.get('snippet', '')[:100]}...")
print()
# Step 2: Get full content of the first search result
if articles:
first_article_url = articles[0].get("url", "")
# Extract slug from URL (format: 2026/05/article-title-slug)
if "/article/" in first_article_url:
article_slug = first_article_url.split("/article/")[-1]
print("=" * 70)
print("FETCHING FULL CONTENT OF TOP ARTICLE")
print("=" * 70)
full_article = client.get_article(article_slug=article_slug)
if full_article.get("status") == "success":
article_data = full_article.get("data", {})
print(f"\nTitle: {article_data.get('title')}")
print(f"Category: {article_data.get('category')}")
print(f"Date: {article_data.get('date')}")
authors = article_data.get("authors", [])
if authors:
author_names = ", ".join([a.get("name") for a in authors])
print(f"Authors: {author_names}")
if article_data.get("image_url"):
print(f"Featured Image: {article_data.get('image_url')}")
content = article_data.get("content", "")
print(f"\nContent (first 500 chars):\n{content[:500]}...")
# Step 3: Browse sports section
print("\n" + "=" * 70)
print("BROWSING SPORTS SECTION")
print("=" * 70)
sports_results = client.list_section_articles(section="sports", page=1, per_page=5)
if sports_results.get("status") == "success":
data = sports_results.get("data", {})
section = data.get("section", "")
total_pages = data.get("total_pages", 0)
articles = data.get("articles", [])
print(f"\nSection: {section.upper()}")
print(f"Total pages: {total_pages}")
print(f"Articles on this page:\n")
for idx, article in enumerate(articles, 1):
print(f"{idx}. {article.get('title')}")
print(f" Date: {article.get('date')}")
authors = article.get("authors", [])
if authors:
print(f" Authors: {', '.join(authors)}")
print()
if __name__ == "__main__":
main()Full-text search across The Middlebury Campus article archives. Returns paginated results ordered by date descending.
| Param | Type | Description |
|---|---|---|
| page | integer | Page number for pagination. |
| queryrequired | string | Search keywords to match against article content and titles. |
| per_page | integer | Number of results per page. |
{
"type": "object",
"fields": {
"page": "integer",
"query": "string",
"total": "integer",
"articles": "array of article summaries with title, url, date, and snippet",
"per_page": "integer"
},
"sample": {
"data": {
"page": 1,
"query": "climate",
"total": 1000,
"articles": [
{
"url": "https://www.middleburycampus.com/article/2026/05/provost-michelle-mccauley-reflects-on-three-decades-at-middlebury",
"date": "05/07/26 1:10pm",
"title": "Provost Michelle McCauley reflects on three decades at Middlebury",
"snippet": "President Ian Baucom announced on Jan. 22 that Executive Vice President and Provost Michelle McCauley will step down from her position on June 30..."
}
],
"per_page": 20
},
"status": "success"
}
}About the middleburycampus.com API
The Middlebury Campus API provides access to The Middlebury Campus student newspaper through 3 endpoints covering article search, full article retrieval, and section browsing. The get_article endpoint returns complete article text alongside authors, publication date, category, featured image URL, and image caption. The search_articles endpoint runs full-text queries against the archive, while list_section_articles lets you paginate through named sections like news, opinion, and sports.
What the API Covers
The Middlebury Campus API exposes content from The Middlebury Campus, the student-run newspaper of Middlebury College in Vermont. All three endpoints return structured data including article titles, URLs, publication dates, and author metadata. Coverage spans current and archived articles across five editorial sections: news, local, arts-culture, opinion, and sports.
Endpoints and Response Fields
get_article accepts an article_slug in YYYY/MM/article-title-slug format and returns a full article object with title, content, category, date, image_url, image_caption, and an authors array where each author object carries a name and slug. This is the primary endpoint for retrieving body text.
search_articles accepts a required query string and optional page and per_page integers. Results are ordered by date descending and each item in the returned articles array includes a title, url, date, and a short snippet. The response also exposes total so you can estimate result set size for pagination.
list_section_articles takes an optional section slug and optional page and per_page parameters. Each article in the response includes title, url, slug, date, authors, and image_url. The response carries total_pages so you can walk the full section archive programmatically.
Limitations and Scope
The API covers publicly accessible article content only. Author profile pages, multimedia galleries, comment threads, and archived print editions are not part of the current endpoint set. Freshness depends on when The Middlebury Campus publishes; the API does not provide real-time push notifications for new articles.
The middleburycampus.com API is a managed, monitored endpoint for www.middleburycampus.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when www.middleburycampus.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official www.middleburycampus.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Monitor Middlebury College campus news by polling
list_section_articleson thenewssection for new publications. - Build a topic tracker that runs
search_articlesqueries on keywords like 'president' or 'housing' to surface relevant coverage over time. - Aggregate student editorial opinion by pulling all articles from the
opinionsection with theirauthorsanddatefields. - Compile a dataset of Middlebury sports coverage by browsing
list_section_articleswithsection=sportsand collectingtitle,date, andimage_url. - Extract full article text via
get_articleto feed a sentiment analysis or NLP pipeline focused on college journalism. - Index Middlebury Campus content into an internal search tool using
search_articlessnippets combined with full text fromget_article. - Track bylines over time by collecting
authorsarrays fromget_articleresponses across a date range.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 100 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 250 req/min |
One credit = one API call regardless of which marketplace API you call. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does The Middlebury Campus have an official developer API?+
What does `get_article` return beyond the article body?+
get_article returns title, date, category, url, content (the full article text), image_url, image_caption, and an authors array. Each author object includes both a name string and a slug for identification. It does not return comment counts or engagement metrics.Can I retrieve articles by a specific author?+
list_section_articles) and full-text search (search_articles), and author name and slug fields are present in article responses. There is no dedicated author-profile or author-filter endpoint. You can fork this API on Parse and revise it to add an endpoint that filters articles by author slug.Does the search endpoint support filtering by date range or section?+
search_articles accepts only a query string, page, and per_page. It does not currently support filtering results by date range or section. The API covers date-ordered full-text search results with a total count for pagination. You can fork this API on Parse and revise it to add date or section filter parameters.How deep does the article archive go?+
page and per_page in both search_articles and list_section_articles allows walking the full available archive.