Activity Feed API (FS-9051)
Overview
The Activity Feed endpoint aggregates recent events for a user's saved GPs (watchlist) and provides a unified timeline of important activities like new filings and new funds.
Endpoint
GET /v1/me/activityAuthentication: Required (JWT Bearer token)
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
types | string | No | All types | Comma-separated activity types to include |
limit | integer | No | 50 | Results per page (max 100) |
offset | integer | No | 0 | Pagination offset |
since | datetime | No | 30 days ago | ISO 8601 datetime for filtering events |
Supported Activity Types
| Type | Description | Data Source |
|---|---|---|
new_filing | New SEC filings (ADV, Form D) | processed_filings table |
new_fund | New funds added | private_funds table |
Response Format
json
{
"data": [
{
"type": "new_filing",
"crd": "123456",
"gp_name": "Blackstone Group",
"event_date": "2025-12-15T10:30:00Z",
"details": {
"form_type": "ADV"
}
},
{
"type": "new_fund",
"crd": "789012",
"gp_name": "KKR",
"event_date": "2025-12-14T15:00:00Z",
"details": {
"fund_name": "KKR Global Impact Fund III",
"fund_id": "805-1234567890"
}
}
],
"meta": {
"total": 127,
"limit": 50,
"offset": 0,
"types_included": ["new_filing", "new_fund"]
}
}Event Schema
Base Event Fields
All events have these common fields:
typescript
{
type: string; // Event type identifier
crd: string; // Adviser CRD number
gp_name: string; // GP legal name
event_date: string; // ISO 8601 timestamp
details: object; // Event-specific data
}Event-Specific Details
new_filing
typescript
{
form_type: string; // "ADV", "ADV/A", or "D"
}new_fund
typescript
{
fund_name: string; // Fund name
fund_id: string; // SEC fund ID (805-XXXXXXXXXX)
}Examples
Get all recent activity (default)
bash
curl -H "Authorization: Bearer $TOKEN" \
https://api.firmhound.com/v1/me/activityFilter by activity type
bash
curl -H "Authorization: Bearer $TOKEN" \
"https://api.firmhound.com/v1/me/activity?types=new_filing"Multiple activity types
bash
curl -H "Authorization: Bearer $TOKEN" \
"https://api.firmhound.com/v1/me/activity?types=new_filing,new_fund"Events from last 7 days
bash
SINCE=$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)
curl -H "Authorization: Bearer $TOKEN" \
"https://api.firmhound.com/v1/me/activity?since=$SINCE"Pagination
bash
# First page
curl -H "Authorization: Bearer $TOKEN" \
"https://api.firmhound.com/v1/me/activity?limit=25&offset=0"
# Second page
curl -H "Authorization: Bearer $TOKEN" \
"https://api.firmhound.com/v1/me/activity?limit=25&offset=25"Error Responses
400 Bad Request - Invalid activity type
json
{
"detail": "Invalid activity types: invalid_type. Valid types: new_filing, new_fund"
}401 Unauthorized - Missing or invalid token
json
{
"detail": "Could not validate credentials"
}500 Internal Server Error
json
{
"detail": "Error fetching activity feed: [error message]"
}Database Schema
The activity feed queries data from:
processed_filings
sql
CREATE TABLE processed_filings (
id SERIAL PRIMARY KEY,
filing_accession VARCHAR(50) UNIQUE NOT NULL,
crd INTEGER NOT NULL,
filing_type VARCHAR(20) NOT NULL,
filing_date DATE NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
processed_at TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ DEFAULT NOW()
);private_funds
sql
CREATE TABLE private_funds (
id SERIAL PRIMARY KEY,
fund_id VARCHAR(50) NOT NULL,
adviser_crd VARCHAR(20) NOT NULL,
name VARCHAR(500),
created_at TIMESTAMPTZ DEFAULT NOW(),
-- ... other fields
);watchlist_items
sql
CREATE TABLE watchlist_items (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
adviser_crd VARCHAR(20),
canonical_gp_id INTEGER,
alert_enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW()
);Implementation Details
Query Strategy
The endpoint uses UNION ALL to combine multiple event sources:
- new_filing events: Joins
processed_filingswithwatchlist_items - new_fund events: Joins
private_fundswithwatchlist_items
Performance Considerations
Indexes: Ensure indexes exist on:
processed_filings.crdprocessed_filings.filing_dateprivate_funds.adviser_crdprivate_funds.created_atwatchlist_items.user_idwatchlist_items.adviser_crd
Query Optimization: The UNION query is wrapped in a CTE for efficient sorting and pagination
Future Enhancements
Planned activity types (not yet implemented):
- aum_change: AUM changes from
adviser_bulk_changestable - search_match: New GPs matching saved searches (requires background job)
- fund_close: Fund status changes to "closed" or "liquidating"
- personnel_change: New executives/directors from
related_persons
Frontend Integration
React Example
typescript
import { useQuery } from '@tanstack/react-query';
interface ActivityEvent {
type: string;
crd: string;
gp_name: string;
event_date: string;
details: any;
}
interface ActivityFeedResponse {
data: ActivityEvent[];
meta: {
total: number;
limit: number;
offset: number;
types_included: string[];
};
}
function useActivityFeed(types?: string[], limit = 50, offset = 0) {
return useQuery<ActivityFeedResponse>({
queryKey: ['activity', types, limit, offset],
queryFn: async () => {
const params = new URLSearchParams({
limit: limit.toString(),
offset: offset.toString(),
});
if (types && types.length > 0) {
params.append('types', types.join(','));
}
const response = await fetch(
`/api/v1/me/activity?${params}`,
{
headers: {
Authorization: `Bearer ${getToken()}`,
},
}
);
if (!response.ok) throw new Error('Failed to fetch activity feed');
return response.json();
},
});
}
// Usage in component
function ActivityFeed() {
const { data, isLoading } = useActivityFeed(['new_filing', 'new_fund']);
if (isLoading) return <div>Loading...</div>;
return (
<div className="activity-feed">
{data?.data.map((event) => (
<ActivityCard key={`${event.type}-${event.crd}-${event.event_date}`} event={event} />
))}
</div>
);
}Testing
See /test_activity_feed.py for comprehensive endpoint tests.
Prerequisites
- Create test user and get JWT token
- Add some GPs to watchlist
- Ensure test data exists in
processed_filingsandprivate_funds
Run Tests
bash
# Set your JWT token
export TOKEN="your_jwt_token_here"
# Run test script
python3 test_activity_feed.pySecurity Considerations
- Authorization: Only returns events for GPs in user's watchlist
- Rate Limiting: Subject to tier-based rate limits
- Data Exposure: GP names are public data from SEC filings
- Performance: Queries limited to max 100 results per request
Related Endpoints
GET /v1/me/watchlist- Manage watchlistGET /v1/me/searches- Manage saved searchesGET /v1/gps/{crd}- Get GP details
Changelog
2025-12-15 (FS-9051)
- Initial implementation with
new_filingandnew_fundevent types - Support for filtering by type, pagination, and date range
- UNION ALL query strategy for aggregating multiple event sources