Skip to content

Latest commit

 

History

History
3674 lines (2855 loc) · 66.6 KB

File metadata and controls

3674 lines (2855 loc) · 66.6 KB

LinkedUp API Documentation

Version: 1.0.0
Base URL: https://api.linkedup.com/api/v1/
Authentication: JWT Bearer Token
Content-Type: application/json


Table of Contents

  1. Quick Start
  2. Authentication
  3. API Response Format
  4. Error Handling
  5. Core Modules
  6. Best Practices
  7. Common Issues & Solutions

Quick Start

Installation & Setup

// Using Fetch API
const API_BASE_URL = 'https://api.linkedup.com/api/v1';

// Initialize API client
const linkedUpAPI = {
  baseURL: API_BASE_URL,
  token: null,
  
  // Set token after login
  setToken(token) {
    this.token = token;
  },
  
  // Make authenticated requests
  async request(method, endpoint, body = null, headers = {}) {
    const options = {
      method,
      headers: {
        'Content-Type': 'application/json',
        ...headers,
      },
    };
    
    if (this.token) {
      options.headers.Authorization = `Bearer ${this.token}`;
    }
    
    if (body) {
      options.body = JSON.stringify(body);
    }
    
    const response = await fetch(`${this.baseURL}${endpoint}`, options);
    return response.json();
  },
  
  // Convenience methods
  get(endpoint) { return this.request('GET', endpoint); },
  post(endpoint, body) { return this.request('POST', endpoint, body); },
  put(endpoint, body) { return this.request('PUT', endpoint, body); },
  delete(endpoint) { return this.request('DELETE', endpoint); },
};

Example: Register & Login

// 1. Register new user
const registerResponse = await linkedUpAPI.post('/auth/register/', {
  username: 'john_doe',
  email: 'john@example.com',
  password: 'SecurePassword123!',
});

// 2. Login
const loginResponse = await linkedUpAPI.post('/auth/login/', {
  email: 'john@example.com',
  password: 'SecurePassword123!',
});

// 3. Store token for future requests
linkedUpAPI.setToken(loginResponse.data.access_token);

// 4. Verify email (if required)
await linkedUpAPI.post('/auth/verify-email/', {
  token: loginResponse.data.verification_token,
});

Authentication

Overview

LinkedUp uses JWT (JSON Web Token) for authentication. All API endpoints require authentication via Bearer token in the Authorization header, except for public endpoints like registration and login.

Token Types

  • Access Token - Short-lived (15 minutes). Used for API requests.
  • Refresh Token - Long-lived (7 days). Used to obtain new access tokens.

HTTP Headers

All authenticated requests must include:

Authorization: Bearer {access_token}
Content-Type: application/json

Token Management

// Decode token to check expiration (client-side, optional)
function parseJwt(token) {
  const base64Url = token.split('.')[1];
  const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
  const jsonPayload = decodeURIComponent(
    atob(base64).split('').map(c => 
      '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
    ).join('')
  );
  return JSON.parse(jsonPayload);
}

// Check if token expires in next 2 minutes
const token = linkedUpAPI.token;
const decoded = parseJwt(token);
const expiresIn = decoded.exp * 1000 - Date.now();

if (expiresIn < 2 * 60 * 1000) {
  // Refresh token
  const newToken = await linkedUpAPI.post('/auth/refresh/', {
    refresh_token: storedRefreshToken,
  });
  linkedUpAPI.setToken(newToken.data.access_token);
}

Token Refresh Flow

async function apiRequestWithAutoRefresh(method, endpoint, body) {
  try {
    return await linkedUpAPI.request(method, endpoint, body);
  } catch (error) {
    if (error.status_code === 401 && error.message.includes('Token expired')) {
      // Refresh token
      const response = await linkedUpAPI.post('/auth/refresh/', {
        refresh_token: localStorage.getItem('refresh_token'),
      });
      
      linkedUpAPI.setToken(response.data.access_token);
      localStorage.setItem('refresh_token', response.data.refresh_token);
      
      // Retry original request
      return linkedUpAPI.request(method, endpoint, body);
    }
    throw error;
  }
}

API Response Format

Success Response

All successful responses follow this format:

{
  "success": true,
  "data": {
    // Response payload varies by endpoint
  },
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total_count": 150,
    "total_pages": 8,
    "has_next": true,
    "has_prev": false
  }
}

Error Response

{
  "error": true,
  "message": "Authentication failed",
  "status_code": 401,
  "errors": {
    // Detailed validation errors (if applicable)
    "email": ["Invalid email format"],
    "password": ["Password must be at least 8 characters"]
  }
}

Example Response Handling

async function handleResponse(response) {
  const json = await response.json();
  
  if (json.error) {
    console.error(`[${json.status_code}] ${json.message}`);
    
    if (json.errors) {
      // Handle validation errors
      Object.entries(json.errors).forEach(([field, messages]) => {
        console.error(`${field}: ${messages.join(', ')}`);
      });
    }
    
    throw new Error(json.message);
  }
  
  return json.data;
}

Error Handling

Common HTTP Status Codes

Status Meaning Description
200 OK Request successful
201 Created Resource created successfully
400 Bad Request Invalid request parameters or validation failed
401 Unauthorized Missing or invalid authentication token
403 Forbidden Authenticated but not authorized for this action
404 Not Found Resource doesn't exist
409 Conflict Resource already exists (e.g., duplicate email)
429 Too Many Requests Rate limit exceeded (max 100 requests/minute)
500 Server Error Internal server error

Error Response Examples

Validation Error (400)

{
  "error": true,
  "message": "Invalid input",
  "status_code": 400,
  "errors": {
    "username": ["Username must be 3-50 characters"],
    "password": ["Password must contain uppercase, lowercase, number, and special character"]
  }
}

Authentication Error (401)

{
  "error": true,
  "message": "Token expired or invalid",
  "status_code": 401
}

Conflict Error (409)

{
  "error": true,
  "message": "Resource already exists",
  "status_code": 409,
  "detail": "Email 'john@example.com' is already registered"
}

Error Handling Best Practices

class APIError extends Error {
  constructor(message, statusCode, errors = null) {
    super(message);
    this.statusCode = statusCode;
    this.errors = errors;
  }
}

async function handleAPIError(response) {
  const json = await response.json();
  
  throw new APIError(
    json.message,
    json.status_code,
    json.errors || null
  );
}

// Usage
try {
  const result = await linkedUpAPI.post('/auth/login/', {
    email: 'invalid',
    password: 'short',
  });
} catch (error) {
  if (error instanceof APIError) {
    if (error.statusCode === 400) {
      // Handle validation errors
      console.error('Validation failed:', error.errors);
    } else if (error.statusCode === 401) {
      // Handle authentication errors
      window.location.href = '/login';
    }
  }
}

Core Modules

AUTH Endpoints

Base URL: /api/v1/auth/

1. Register User

POST /auth/register/

Description: Create a new user account

Request Body:

{
  "username": "john_doe",
  "email": "john@example.com",
  "password": "SecurePassword123!",
  "password_confirm": "SecurePassword123!"
}

Response (201 Created):

{
  "success": true,
  "data": {
    "user_id": 123,
    "username": "john_doe",
    "email": "john@example.com",
    "created_at": "2026-01-21T10:30:00Z",
    "verification_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "message": "User registered. Please verify your email."
  }
}

Password Requirements:

  • Minimum 8 characters
  • At least one uppercase letter
  • At least one lowercase letter
  • At least one number
  • At least one special character (!@#$%^&*)

Validation Errors (400):

{
  "error": true,
  "message": "Invalid input",
  "status_code": 400,
  "errors": {
    "username": ["Username must be 3-50 characters", "This username is already taken"],
    "email": ["Invalid email format"],
    "password": ["Password must contain special characters"]
  }
}

JavaScript Example:

async function registerUser() {
  try {
    const response = await linkedUpAPI.post('/auth/register/', {
      username: 'john_doe',
      email: 'john@example.com',
      password: 'SecurePassword123!',
      password_confirm: 'SecurePassword123!',
    });
    
    console.log('Registration successful:', response.user_id);
    console.log('Verification email sent to:', response.email);
    
    // Store verification token for email confirmation
    localStorage.setItem('verificationToken', response.verification_token);
    
  } catch (error) {
    console.error('Registration failed:', error.message);
    displayErrorMessage(error.message);
  }
}

2. Login User

POST /auth/login/

Description: Authenticate user and get JWT tokens

Request Body:

{
  "email": "john@example.com",
  "password": "SecurePassword123!"
}

Response (200 OK):

{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
      "user_id": 123,
      "username": "john_doe",
      "email": "john@example.com",
      "profile_picture": "https://cdn.linkedup.com/profiles/123.jpg",
      "is_premium": false,
      "created_at": "2026-01-21T10:30:00Z"
    },
    "expires_in": 900
  }
}

Error Responses:

// 401 - Invalid credentials
{
  "error": true,
  "message": "Authentication failed",
  "status_code": 401
}

// 403 - Account blocked/suspended
{
  "error": true,
  "message": "Account is temporarily locked",
  "status_code": 403
}

JavaScript Example:

async function loginUser(email, password) {
  try {
    const response = await linkedUpAPI.post('/auth/login/', {
      email,
      password,
    });
    
    // Store tokens
    localStorage.setItem('accessToken', response.access_token);
    localStorage.setItem('refreshToken', response.refresh_token);
    
    // Set in API client
    linkedUpAPI.setToken(response.access_token);
    
    // Store user info
    localStorage.setItem('currentUser', JSON.stringify(response.user));
    
    // Redirect to dashboard
    window.location.href = '/dashboard';
    
  } catch (error) {
    if (error.status_code === 401) {
      showErrorMessage('Invalid email or password');
    } else if (error.status_code === 403) {
      showErrorMessage('Your account is locked. Contact support.');
    }
  }
}

3. Logout User

POST /auth/logout/

Description: Invalidate user session

Authentication: Required ✅

Request Body:

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Logged out successfully"
  }
}

JavaScript Example:

async function logoutUser() {
  try {
    await linkedUpAPI.post('/auth/logout/', {
      refresh_token: localStorage.getItem('refreshToken'),
    });
    
    // Clear local storage
    localStorage.removeItem('accessToken');
    localStorage.removeItem('refreshToken');
    localStorage.removeItem('currentUser');
    
    // Clear token from API client
    linkedUpAPI.setToken(null);
    
    // Redirect to login
    window.location.href = '/login';
    
  } catch (error) {
    console.error('Logout failed:', error.message);
  }
}

4. Refresh Token

POST /auth/refresh/

Description: Get new access token using refresh token

Request Body:

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response (200 OK):

{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 900
  }
}

JavaScript Example:

async function refreshAccessToken() {
  try {
    const response = await linkedUpAPI.post('/auth/refresh/', {
      refresh_token: localStorage.getItem('refreshToken'),
    });
    
    localStorage.setItem('accessToken', response.access_token);
    localStorage.setItem('refreshToken', response.refresh_token);
    linkedUpAPI.setToken(response.access_token);
    
    return response.access_token;
    
  } catch (error) {
    // Refresh token expired, redirect to login
    window.location.href = '/login';
  }
}

5. Verify Email

POST /auth/verify-email/

Description: Confirm user email address

Request Body:

{
  "verification_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Email verified successfully"
  }
}

JavaScript Example:

async function verifyEmail(token) {
  try {
    await linkedUpAPI.post('/auth/verify-email/', {
      verification_token: token,
    });
    
    console.log('Email verified successfully');
    
  } catch (error) {
    console.error('Email verification failed:', error.message);
  }
}

6. Resend Verification Email

POST /auth/verify-email/resend/

Description: Send verification email again

Request Body:

{
  "email": "john@example.com"
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Verification email sent"
  }
}

7. Password Reset Request

POST /auth/password/reset/

Description: Initiate password reset process

Request Body:

{
  "email": "john@example.com"
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Password reset email sent",
    "reset_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

JavaScript Example:

async function requestPasswordReset(email) {
  try {
    const response = await linkedUpAPI.post('/auth/password/reset/', {
      email,
    });
    
    console.log('Reset email sent to:', email);
    return response.reset_token;
    
  } catch (error) {
    if (error.status_code === 404) {
      // Don't reveal if email exists
      console.log('If email exists, reset link will be sent');
    }
  }
}

8. Confirm Password Reset

POST /auth/password/reset/confirm/

Description: Complete password reset

Request Body:

{
  "reset_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "new_password": "NewSecurePassword123!",
  "new_password_confirm": "NewSecurePassword123!"
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Password reset successful"
  }
}

9. Change Password (Authenticated)

POST /auth/password/change/

Description: Change password for authenticated user

Authentication: Required ✅

Request Body:

{
  "current_password": "OldPassword123!",
  "new_password": "NewSecurePassword456!",
  "new_password_confirm": "NewSecurePassword456!"
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Password changed successfully"
  }
}

10. Deactivate Account

POST /auth/deactivate/

Description: Deactivate user account

Authentication: Required ✅

Request Body:

{
  "password": "SecurePassword123!",
  "reason": "Taking a break"
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Account deactivated successfully",
    "reactivation_deadline": "2026-04-21T10:30:00Z"
  }
}

USERS Endpoints

Base URL: /api/v1/users/

1. Get User Profile

GET /users/{username}/

Description: Get public user profile information

Authentication: Optional (more data if authenticated)

Path Parameters:

Parameter Type Description
username string Target user's username

Response (200 OK):

{
  "success": true,
  "data": {
    "user_id": 456,
    "username": "jane_smith",
    "first_name": "Jane",
    "last_name": "Smith",
    "bio": "Software engineer | Tech enthusiast",
    "profile_picture": "https://cdn.linkedup.com/profiles/456.jpg",
    "banner": "https://cdn.linkedup.com/banners/456.jpg",
    "location": "San Francisco, CA",
    "website": "https://janesmith.dev",
    "verified": true,
    "is_premium": true,
    "followers_count": 1250,
    "following_count": 380,
    "posts_count": 145,
    "joined_date": "2024-06-15T08:20:00Z",
    "last_active": "2026-01-21T09:45:00Z"
  }
}

JavaScript Example:

async function getUserProfile(username) {
  try {
    const user = await linkedUpAPI.get(`/users/${username}/`);
    displayUserProfile(user);
  } catch (error) {
    if (error.status_code === 404) {
      showErrorMessage(`User '${username}' not found`);
    }
  }
}

2. Get User Contact Information

GET /users/{username}/contact-info/

Description: Get user's contact details (email, phone - if public)

Authentication: Required ✅

Path Parameters:

Parameter Type Description
username string Target user's username

Response (200 OK):

{
  "success": true,
  "data": {
    "email": "jane@example.com",
    "phone": "+1-555-0123",
    "contact_preference": "email",
    "is_email_public": false,
    "is_phone_public": true
  }
}

3. Follow User

POST /users/{username}/follow/

Description: Follow a user

Authentication: Required ✅

Path Parameters:

Parameter Type Description
username string Username to follow

Request Body:

{
  "notify": true
}

Response (201 Created):

{
  "success": true,
  "data": {
    "message": "Following jane_smith",
    "following": true,
    "user": {
      "username": "jane_smith",
      "followers_count": 1251
    }
  }
}

Error Responses:

// 409 - Already following
{
  "error": true,
  "message": "Resource already exists",
  "status_code": 409,
  "detail": "Already following this user"
}

// 403 - User blocked you or is private
{
  "error": true,
  "message": "Not authorized",
  "status_code": 403
}

JavaScript Example:

async function followUser(username) {
  try {
    await linkedUpAPI.post(`/users/${username}/follow/`, {
      notify: true,
    });
    
    console.log(`Now following ${username}`);
    updateFollowButton(username, 'following');
    
  } catch (error) {
    if (error.status_code === 409) {
      showMessage('Already following this user');
    }
  }
}

4. Unfollow User

POST /users/{username}/unfollow/

Description: Unfollow a user

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Unfollowed jane_smith",
    "following": false,
    "user": {
      "username": "jane_smith",
      "followers_count": 1249
    }
  }
}

5. Block User

POST /users/{username}/block/

Description: Block a user

Authentication: Required ✅

Response (201 Created):

{
  "success": true,
  "data": {
    "message": "User blocked",
    "blocked": true
  }
}

6. Unblock User

POST /users/{username}/unblock/

Description: Unblock a previously blocked user

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "User unblocked",
    "blocked": false
  }
}

7. Get User's Posts

GET /users/{username}/posts/

Description: Get all posts from a specific user

Authentication: Optional

Path Parameters:

Parameter Type Description
username string Username to fetch posts from

Query Parameters:

Parameter Type Default Description
page integer 1 Page number for pagination
page_size integer 20 Posts per page (max 100)
sort string newest Sort order: newest, oldest, trending

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "post_id": 789,
      "post_uid": "post_abc123xyz",
      "author": {
        "user_id": 456,
        "username": "jane_smith",
        "profile_picture": "https://cdn.linkedup.com/profiles/456.jpg"
      },
      "content": "Just launched my new project!",
      "images": [
        {
          "url": "https://cdn.linkedup.com/posts/789/image1.jpg",
          "width": 1200,
          "height": 630
        }
      ],
      "created_at": "2026-01-20T15:30:00Z",
      "updated_at": "2026-01-20T15:30:00Z",
      "visibility": "public",
      "engagement": {
        "likes_count": 42,
        "comments_count": 8,
        "shares_count": 5,
        "views_count": 230
      },
      "is_liked": false,
      "is_saved": true
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total_count": 145,
    "total_pages": 8,
    "has_next": true,
    "has_prev": false
  }
}

JavaScript Example:

async function getUserPosts(username, page = 1) {
  try {
    const response = await linkedUpAPI.get(
      `/users/${username}/posts/?page=${page}&page_size=20&sort=newest`
    );
    
    displayPosts(response.data);
    displayPagination(response.pagination);
    
  } catch (error) {
    console.error('Failed to load posts:', error.message);
  }
}

8. Get User's Followers

GET /users/{username}/followers/

Description: Get list of user's followers

Authentication: Optional

Query Parameters:

Parameter Type Default Description
page integer 1 Page number
page_size integer 50 Followers per page (max 100)
sort string newest Sort: newest, oldest, alphabetical

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "user_id": 123,
      "username": "john_doe",
      "profile_picture": "https://cdn.linkedup.com/profiles/123.jpg",
      "bio": "Full-stack developer",
      "is_following": false,
      "followed_at": "2025-12-10T14:20:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 50,
    "total_count": 1250,
    "total_pages": 25,
    "has_next": true,
    "has_prev": false
  }
}

9. Get User's Following

GET /users/{username}/following/

Description: Get list of users that this user is following

Authentication: Optional

Response: Similar to followers endpoint


10. Get Relationship State

GET /users/{username}/relationship-state/

Description: Get relationship status with another user

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "username": "jane_smith",
    "is_following": true,
    "is_followed_by": false,
    "is_blocked": false,
    "is_blocked_by": false,
    "can_message": true,
    "relationship_status": "following"
  }
}

PROFILE Endpoints

Base URL: /api/v1/profile/

1. Get My Profile

GET /profile/me/{username}/

Description: Get authenticated user's own profile

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "user_id": 123,
    "username": "john_doe",
    "email": "john@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "bio": "Full-stack developer | Open source enthusiast",
    "profile_picture": "https://cdn.linkedup.com/profiles/123.jpg",
    "banner": "https://cdn.linkedup.com/banners/123.jpg",
    "location": "New York, NY",
    "website": "https://johndoe.dev",
    "verified": true,
    "is_premium": true,
    "followers_count": 850,
    "following_count": 420,
    "posts_count": 156,
    "is_public_profile": true,
    "email_notifications_enabled": true,
    "push_notifications_enabled": true,
    "created_at": "2024-03-10T09:15:00Z"
  }
}

2. Update My Profile

PUT /profile/me/{username}/

Description: Update authenticated user's profile

Authentication: Required ✅

Request Body:

{
  "first_name": "Jonathan",
  "last_name": "Doe",
  "bio": "Senior Full-stack Developer",
  "location": "San Francisco, CA",
  "website": "https://jonathandoe.dev",
  "is_public_profile": true
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Profile updated successfully",
    "user": {
      "username": "john_doe",
      "first_name": "Jonathan",
      "bio": "Senior Full-stack Developer"
    }
  }
}

JavaScript Example:

async function updateProfile(formData) {
  try {
    const currentUser = JSON.parse(localStorage.getItem('currentUser'));
    
    const response = await linkedUpAPI.put(
      `/profile/me/${currentUser.username}/`,
      {
        first_name: formData.firstName,
        last_name: formData.lastName,
        bio: formData.bio,
        location: formData.location,
        website: formData.website,
      }
    );
    
    console.log('Profile updated:', response.user);
    showSuccessMessage('Profile updated successfully');
    
  } catch (error) {
    console.error('Update failed:', error.message);
  }
}

3. Update Profile Picture

POST /profile/me/{username}/

Description: Upload new profile picture

Authentication: Required ✅

Request (Multipart Form Data):

Content-Type: multipart/form-data

profile_picture: <file>

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Profile picture updated",
    "profile_picture": "https://cdn.linkedup.com/profiles/123_v2.jpg"
  }
}

JavaScript Example:

async function uploadProfilePicture(file) {
  const formData = new FormData();
  formData.append('profile_picture', file);
  
  const response = await fetch(
    `${linkedUpAPI.baseURL}/profile/me/${currentUsername}/`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${linkedUpAPI.token}`,
      },
      body: formData,
    }
  );
  
  return response.json();
}

4. Get My Posts

GET /profile/me/{username}/posts/

Description: Get authenticated user's posts

Authentication: Required ✅

Query Parameters:

Parameter Type Default Description
page integer 1 Page number
page_size integer 20 Posts per page
visibility string all Filter: all, public, private
status string all Filter: all, published, draft

Response: Similar to Users posts endpoint


5. Get My Comments

GET /profile/me/{username}/comments/

Description: Get all comments made by authenticated user

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "comment_id": 234,
      "comment_uid": "comment_def456ghi",
      "post": {
        "post_uid": "post_abc123xyz",
        "content_preview": "Just launched..."
      },
      "content": "Great project! Love the implementation.",
      "likes_count": 5,
      "replies_count": 2,
      "created_at": "2026-01-21T08:15:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total_count": 42,
    "total_pages": 3,
    "has_next": true,
    "has_prev": false
  }
}

6. Get My Analytics

GET /profile/me/{username}/analytics/

Description: Get profile analytics (requires premium)

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "period": "last_30_days",
    "impressions": 5420,
    "profile_views": 320,
    "followers_gained": 45,
    "posts_engagement": {
      "total_likes": 156,
      "total_comments": 32,
      "total_shares": 12
    }
  }
}

POSTS Endpoints

Base URL: /api/v1/posts/

1. Create Post

POST /posts/

Description: Create a new post

Authentication: Required ✅

Request Body:

{
  "content": "Just completed an amazing project! #webdevelopment",
  "visibility": "public",
  "allow_comments": true,
  "allow_reactions": true,
  "images": [
    {
      "url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABA..."
    }
  ],
  "tagged_users": ["jane_smith", "bob_wilson"],
  "hashtags": ["webdevelopment", "javascript"],
  "polls": {
    "question": "Favorite framework?",
    "options": ["React", "Vue", "Angular"],
    "expires_at": "2026-01-28T10:00:00Z"
  }
}

Response (201 Created):

{
  "success": true,
  "data": {
    "post_id": 1001,
    "post_uid": "post_xyz789abc",
    "author": {
      "user_id": 123,
      "username": "john_doe"
    },
    "content": "Just completed an amazing project!",
    "visibility": "public",
    "created_at": "2026-01-21T10:30:00Z",
    "engagement": {
      "likes_count": 0,
      "comments_count": 0
    }
  }
}

Validation Errors (400):

{
  "error": true,
  "message": "Invalid input",
  "status_code": 400,
  "errors": {
    "content": ["Content is required"],
    "images": ["Each image must be less than 5MB"]
  }
}

JavaScript Example:

async function createPost(content, images = []) {
  try {
    const postData = {
      content,
      visibility: 'public',
      allow_comments: true,
      allow_reactions: true,
    };
    
    if (images.length > 0) {
      postData.images = await Promise.all(
        images.map(file => fileToBase64(file))
      );
    }
    
    const response = await linkedUpAPI.post('/posts/', postData);
    
    console.log('Post created:', response.post_uid);
    return response;
    
  } catch (error) {
    console.error('Failed to create post:', error.message);
  }
}

function fileToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve({ url: reader.result });
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

2. Get Post Details

GET /posts/{post_uid}/

Description: Get full post details

Authentication: Optional

Path Parameters:

Parameter Type Description
post_uid string Unique post identifier

Response (200 OK):

{
  "success": true,
  "data": {
    "post_id": 1001,
    "post_uid": "post_xyz789abc",
    "author": {
      "user_id": 123,
      "username": "john_doe",
      "profile_picture": "https://cdn.linkedup.com/profiles/123.jpg"
    },
    "content": "Just completed an amazing project! #webdevelopment",
    "content_html": "<p>Just completed an amazing project! <a href='...'>#webdevelopment</a></p>",
    "images": [
      {
        "url": "https://cdn.linkedup.com/posts/1001/image1.jpg",
        "width": 1200,
        "height": 630,
        "alt": "Project screenshot"
      }
    ],
    "visibility": "public",
    "allow_comments": true,
    "allow_reactions": true,
    "created_at": "2026-01-20T15:30:00Z",
    "updated_at": "2026-01-20T15:30:00Z",
    "is_edited": false,
    "engagement": {
      "likes_count": 42,
      "comments_count": 8,
      "shares_count": 5,
      "views_count": 230
    },
    "is_liked": false,
    "is_saved": true,
    "top_reactions": [
      {
        "reaction_type": "like",
        "count": 35,
        "users": [
          {
            "username": "jane_smith",
            "profile_picture": "https://cdn.linkedup.com/profiles/456.jpg"
          }
        ]
      }
    ],
    "poll": {
      "poll_id": 555,
      "question": "Favorite framework?",
      "options": [
        {
          "id": 1,
          "text": "React",
          "votes": 45,
          "percentage": 65,
          "is_voted": true
        },
        {
          "id": 2,
          "text": "Vue",
          "votes": 18,
          "percentage": 26,
          "is_voted": false
        }
      ],
      "total_votes": 69,
      "is_closed": false,
      "expires_at": "2026-01-28T10:00:00Z"
    }
  }
}

3. Edit Post

PUT /posts/{post_uid}/edit/

Description: Edit existing post (only by author)

Authentication: Required ✅

Request Body:

{
  "content": "Updated content with more details!",
  "visibility": "public"
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Post updated successfully",
    "post": {
      "post_uid": "post_xyz789abc",
      "content": "Updated content with more details!",
      "is_edited": true,
      "edited_at": "2026-01-21T11:45:00Z"
    }
  }
}

4. Delete Post

DELETE /posts/{post_uid}/delete/

Description: Delete a post

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Post deleted successfully"
  }
}

5. Update Post Visibility

PUT /posts/{post_uid}/visibility/

Description: Change post visibility

Authentication: Required ✅

Request Body:

{
  "visibility": "private"
}

Visibility Options:

  • public - Everyone can see
  • private - Only followers can see
  • friends - Only accepted connections can see
  • hidden - No one except author can see

6. Get Post Stats

GET /posts/{post_uid}/stats/

Description: Get detailed post statistics

Authentication: Optional (more data if author)

Response (200 OK):

{
  "success": true,
  "data": {
    "views": 230,
    "impressions": 1200,
    "engagement_rate": 22.4,
    "top_traffic_source": "feed",
    "demographics": {
      "gender": {
        "male": 55,
        "female": 40,
        "other": 5
      },
      "age_groups": {
        "18-24": 120,
        "25-34": 85,
        "35-44": 20
      }
    }
  }
}

7. Report Post

POST /posts/{post_uid}/report/

Description: Report inappropriate post

Authentication: Required ✅

Request Body:

{
  "reason": "inappropriate_content",
  "description": "This post contains offensive language"
}

Report Reasons:

  • spam - Spam or promotional content
  • harassment - Harassment or bullying
  • inappropriate_content - Inappropriate content
  • copyright - Copyright violation
  • misinformation - False or misleading information
  • other - Other

COMMENTS Endpoints

Base URL: /api/v1/comments/

1. Create Comment

POST /comments/

Description: Add comment to a post

Authentication: Required ✅

Request Body:

{
  "post_uid": "post_xyz789abc",
  "content": "This is a great post!",
  "parent_comment_uid": null
}

Response (201 Created):

{
  "success": true,
  "data": {
    "comment_id": 234,
    "comment_uid": "comment_def456ghi",
    "post_uid": "post_xyz789abc",
    "author": {
      "user_id": 123,
      "username": "john_doe",
      "profile_picture": "https://cdn.linkedup.com/profiles/123.jpg"
    },
    "content": "This is a great post!",
    "created_at": "2026-01-21T10:45:00Z",
    "likes_count": 0,
    "replies_count": 0
  }
}

JavaScript Example:

async function addComment(postUid, content) {
  try {
    const response = await linkedUpAPI.post('/comments/', {
      post_uid: postUid,
      content,
      parent_comment_uid: null,
    });
    
    console.log('Comment added:', response.comment_uid);
    return response;
    
  } catch (error) {
    console.error('Failed to add comment:', error.message);
  }
}

2. Get Comment Details

GET /comments/{comment_uid}/

Description: Get full comment with replies

Response (200 OK):

{
  "success": true,
  "data": {
    "comment_id": 234,
    "comment_uid": "comment_def456ghi",
    "post_uid": "post_xyz789abc",
    "author": {
      "user_id": 123,
      "username": "john_doe",
      "profile_picture": "https://cdn.linkedup.com/profiles/123.jpg"
    },
    "content": "This is a great post!",
    "created_at": "2026-01-21T10:45:00Z",
    "updated_at": "2026-01-21T10:45:00Z",
    "is_edited": false,
    "likes_count": 5,
    "replies_count": 2,
    "is_liked": false
  }
}

3. Delete Comment

DELETE /comments/{comment_uid}/delete/

Description: Delete a comment

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Comment deleted successfully"
  }
}

4. Reply to Comment

POST /comments/{comment_uid}/replies/

Description: Add reply to a comment

Authentication: Required ✅

Request Body:

{
  "content": "I agree with you!"
}

Response (201 Created):

{
  "success": true,
  "data": {
    "comment_id": 235,
    "comment_uid": "comment_jkl789mno",
    "parent_comment_uid": "comment_def456ghi",
    "content": "I agree with you!",
    "created_at": "2026-01-21T11:00:00Z"
  }
}

REACTIONS Endpoints

Base URL: /api/v1/reactions/

1. Toggle Reaction

POST /reactions/toggle/

Description: Like/unlike or react to a post/comment

Authentication: Required ✅

Request Body:

{
  "target_type": "post",
  "target_uid": "post_xyz789abc",
  "reaction_type": "like"
}

Reaction Types:

  • like - Like
  • love - Love
  • haha - Funny
  • wow - Wow
  • sad - Sad
  • angry - Angry

Target Types:

  • post - Reaction on post
  • comment - Reaction on comment

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Reaction added",
    "is_reacted": true,
    "reaction_type": "like",
    "total_reactions": 43
  }
}

JavaScript Example:

async function toggleLike(postUid) {
  try {
    const response = await linkedUpAPI.post('/reactions/toggle/', {
      target_type: 'post',
      target_uid: postUid,
      reaction_type: 'like',
    });
    
    console.log('Like toggled:', response.is_reacted);
    updateLikeButton(postUid, response.is_reacted);
    
  } catch (error) {
    console.error('Failed to toggle like:', error.message);
  }
}

2. Get Reactions List

GET /reactions/list/

Description: Get all reactions on a post/comment

Authentication: Optional

Query Parameters:

Parameter Type Description
target_type string post or comment
target_uid string UID of target
reaction_type string Filter by reaction type (optional)

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "reaction_type": "like",
      "count": 35,
      "users": [
        {
          "user_id": 456,
          "username": "jane_smith",
          "profile_picture": "https://cdn.linkedup.com/profiles/456.jpg"
        }
      ]
    },
    {
      "reaction_type": "love",
      "count": 8,
      "users": [...]
    }
  ]
}

FEED Endpoints

Base URL: /api/v1/feed/

1. Get Main Feed

GET /feed/

Description: Get personalized main feed

Authentication: Required ✅

Query Parameters:

Parameter Type Default Description
page integer 1 Page number
page_size integer 20 Posts per page (max 50)
filter string all Filter: all, connections_only, following

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "post_id": 1001,
      "post_uid": "post_xyz789abc",
      "author": {
        "user_id": 456,
        "username": "jane_smith"
      },
      "content": "Just launched my new project!",
      "created_at": "2026-01-20T15:30:00Z",
      "engagement": {
        "likes_count": 42,
        "comments_count": 8
      },
      "is_liked": false
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total_count": 450,
    "has_next": true
  }
}

JavaScript Example:

async function loadMainFeed(page = 1) {
  try {
    const response = await linkedUpAPI.get(
      `/feed/?page=${page}&page_size=20&filter=all`
    );
    
    displayFeedPosts(response.data);
    setupInfiniteScroll(response.pagination);
    
  } catch (error) {
    console.error('Failed to load feed:', error.message);
  }
}

// Infinite scroll implementation
let currentPage = 1;
window.addEventListener('scroll', async () => {
  if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 500) {
    currentPage++;
    await loadMainFeed(currentPage);
  }
});

2. Get Following Feed

GET /feed/following/

Description: Feed from users you follow only

Authentication: Required ✅

Query Parameters: Same as main feed


3. Get Trending Feed

GET /feed/trending/

Description: Trending posts

Authentication: Optional

Query Parameters:

Parameter Type Default Description
time_range string 24h 1h, 6h, 24h, 7d, 30d
page integer 1 Page number
page_size integer 20 Posts per page

4. Get User Feed

GET /feed/user/{username}/

Description: Feed for a specific user's posts

Authentication: Optional


NOTIFICATIONS Endpoints

Base URL: /api/v1/notifications/

1. Get Notifications

GET /notifications/

Description: Get user's notifications

Authentication: Required ✅

Query Parameters:

Parameter Type Default Description
page integer 1 Page number
page_size integer 20 Notifications per page
unread_only boolean false Show only unread
type string all Filter: all, like, comment, follow, message

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "notification_id": 5001,
      "notification_uid": "notif_abc123",
      "type": "like",
      "actor": {
        "user_id": 456,
        "username": "jane_smith",
        "profile_picture": "https://cdn.linkedup.com/profiles/456.jpg"
      },
      "action": "liked your post",
      "target": {
        "type": "post",
        "target_uid": "post_xyz789abc",
        "preview": "Just launched my new project!"
      },
      "is_read": false,
      "created_at": "2026-01-21T10:20:00Z",
      "action_url": "/posts/post_xyz789abc"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total_count": 42,
    "total_pages": 3,
    "has_next": true
  }
}

JavaScript Example:

async function loadNotifications() {
  try {
    const response = await linkedUpAPI.get(
      '/notifications/?page=1&page_size=20&unread_only=false'
    );
    
    displayNotifications(response.data);
    
  } catch (error) {
    console.error('Failed to load notifications:', error.message);
  }
}

// Real-time notifications with WebSocket
const ws = new WebSocket('wss://api.linkedup.com/ws/notifications/');

ws.onmessage = (event) => {
  const notification = JSON.parse(event.data);
  showNotificationToast(notification);
  addNotificationToList(notification);
};

2. Get Unread Count

GET /notifications/unread-count/

Description: Get count of unread notifications

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "unread_count": 5,
    "unread_by_type": {
      "like": 2,
      "comment": 1,
      "follow": 1,
      "message": 1
    }
  }
}

JavaScript Example:

async function getUnreadCount() {
  try {
    const response = await linkedUpAPI.get('/notifications/unread-count/');
    updateNotificationBadge(response.unread_count);
  } catch (error) {
    console.error('Failed to get unread count:', error.message);
  }
}

// Poll every 30 seconds
setInterval(getUnreadCount, 30000);

3. Mark Notification as Read

POST /notifications/{notification_uid}/read/

Description: Mark single notification as read

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Notification marked as read",
    "is_read": true
  }
}

4. Mark All as Read

POST /notifications/mark-all-read/

Description: Mark all notifications as read

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "All notifications marked as read",
    "marked_count": 12
  }
}

5. Notification Settings

GET /notifications/settings/

Description: Get notification preferences

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "email_notifications": {
      "likes": true,
      "comments": true,
      "follows": true,
      "messages": true,
      "marketing": false
    },
    "push_notifications": {
      "likes": true,
      "comments": true,
      "follows": false,
      "messages": true
    },
    "frequency": "instant"
  }
}

Update Notification Settings

PUT /notifications/settings/

Request Body:

{
  "email_notifications": {
    "likes": false,
    "comments": true,
    "follows": true,
    "messages": true,
    "marketing": false
  }
}

SEARCH Endpoints

Base URL: /api/v1/search/

1. Global Search

GET /search/

Description: Search across all content

Authentication: Optional

Query Parameters:

Parameter Type Required Description
q string Yes Search query
type string No Filter: all, users, posts, hashtags
page integer No Page number (default 1)
page_size integer No Results per page (default 20)

Response (200 OK):

{
  "success": true,
  "data": {
    "users": [
      {
        "user_id": 456,
        "username": "jane_smith",
        "profile_picture": "https://cdn.linkedup.com/profiles/456.jpg",
        "bio": "Software engineer",
        "followers_count": 1250
      }
    ],
    "posts": [
      {
        "post_uid": "post_xyz789abc",
        "author": {
          "username": "jane_smith"
        },
        "content": "Search result post content",
        "created_at": "2026-01-20T15:30:00Z"
      }
    ],
    "hashtags": [
      {
        "tag": "javascript",
        "post_count": 2340,
        "trend_position": 5
      }
    ]
  },
  "pagination": {
    "page": 1,
    "page_size": 20
  }
}

JavaScript Example:

async function globalSearch(query, type = 'all') {
  try {
    const response = await linkedUpAPI.get(
      `/search/?q=${encodeURIComponent(query)}&type=${type}&page_size=30`
    );
    
    displaySearchResults(response.data);
    
  } catch (error) {
    console.error('Search failed:', error.message);
  }
}

// Real-time search with debounce
let searchTimeout;
document.getElementById('searchInput').addEventListener('input', (e) => {
  clearTimeout(searchTimeout);
  searchTimeout = setTimeout(() => {
    globalSearch(e.target.value);
  }, 300);
});

2. Search Users

GET /search/users/

Description: Search for users

Authentication: Optional

Query Parameters:

Parameter Type Description
q string Search query (username, name, bio)
page integer Page number
page_size integer Results per page

Response: Similar to global search users results


3. Search Posts

GET /search/posts/

Description: Search posts by content

Authentication: Optional

Query Parameters:

Parameter Type Description
q string Search query
author string Filter by author username
hashtag string Filter by hashtag
date_from string From date (ISO 8601)
date_to string To date (ISO 8601)

4. Get Search Suggestions

GET /search/suggestions/

Description: Get autocomplete suggestions

Authentication: Optional

Query Parameters:

Parameter Type Description
q string Partial query
type string users, posts, hashtags

Response (200 OK):

{
  "success": true,
  "data": {
    "suggestions": [
      {
        "type": "user",
        "username": "jane_smith",
        "profile_picture": "https://cdn.linkedup.com/profiles/456.jpg"
      },
      {
        "type": "hashtag",
        "tag": "javascript"
      }
    ]
  }
}

NETWORK Endpoints

Base URL: /api/v1/network/

1. Get Network Dashboard

GET /network/

Description: Get network overview

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "connections_count": 248,
    "pending_requests_count": 5,
    "received_requests_count": 3,
    "suggestions_count": 12,
    "stats": {
      "total_interactions": 450,
      "new_connections_this_month": 15
    }
  }
}

2. Get Received Connection Requests

GET /network/requests/received/

Description: Get pending connection requests received

Authentication: Required ✅

Query Parameters:

Parameter Type Default Description
page integer 1 Page number
page_size integer 20 Results per page
status string pending pending, accepted, ignored

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "request_id": 789,
      "from_user": {
        "user_id": 789,
        "username": "alice_wonder",
        "profile_picture": "https://cdn.linkedup.com/profiles/789.jpg",
        "bio": "Designer & artist",
        "mutual_connections": 5
      },
      "message": "Let's connect!",
      "created_at": "2026-01-20T14:30:00Z",
      "status": "pending"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total_count": 3
  }
}

3. Get Sent Connection Requests

GET /network/requests/sent/

Description: Get pending requests you sent

Authentication: Required ✅


4. Accept Connection Request

POST /network/requests/{username}/accept/

Description: Accept a connection request

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Connection accepted",
    "connection": {
      "user_id": 789,
      "username": "alice_wonder",
      "profile_picture": "https://cdn.linkedup.com/profiles/789.jpg"
    }
  }
}

5. Ignore Connection Request

POST /network/requests/{username}/ignore/

Description: Ignore/decline a connection request

Authentication: Required ✅


6. Get Connections List

GET /network/connections/

Description: Get list of all connections

Authentication: Required ✅

Query Parameters:

Parameter Type Default Description
page integer 1 Page number
page_size integer 50 Results per page
sort string newest newest, alphabetical
filter string all all, active, inactive

7. Get Connection Suggestions

GET /network/suggestions/

Description: Get suggested connections

Authentication: Required ✅

Query Parameters:

Parameter Type Description
limit integer Number of suggestions (max 50)

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "user_id": 900,
      "username": "bob_builder",
      "profile_picture": "https://cdn.linkedup.com/profiles/900.jpg",
      "bio": "Software architect",
      "mutual_connections": 8,
      "reason": "5 mutual connections"
    }
  ]
}

PREMIUM Endpoints

Base URL: /api/v1/premium/

1. Get Premium Status

GET /premium/status/

Description: Get current premium subscription status

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": {
    "is_premium": true,
    "plan": "pro",
    "subscription_id": "sub_12345",
    "started_at": "2025-10-15T10:00:00Z",
    "renews_at": "2026-10-15T10:00:00Z",
    "cancels_at": null,
    "status": "active",
    "features": [
      "analytics",
      "advanced_search",
      "priority_support",
      "custom_domain"
    ]
  }
}

2. Upgrade to Premium

POST /premium/upgrade/

Description: Start premium subscription

Authentication: Required ✅

Request Body:

{
  "plan": "pro",
  "payment_method_id": "pm_1234567890",
  "billing_cycle": "monthly"
}

Billing Cycles:

  • monthly - $9.99/month
  • annual - $99.99/year (save 17%)

Response (201 Created):

{
  "success": true,
  "data": {
    "message": "Premium subscription activated",
    "subscription": {
      "subscription_id": "sub_new_12345",
      "plan": "pro",
      "status": "active",
      "next_billing_date": "2026-02-21T10:00:00Z",
      "price": 9.99,
      "currency": "USD"
    }
  }
}

3. Cancel Premium

POST /premium/cancel/

Description: Cancel premium subscription

Authentication: Required ✅

Request Body:

{
  "reason": "Too expensive",
  "feedback": "Good service but doesn't fit my needs"
}

Response (200 OK):

{
  "success": true,
  "data": {
    "message": "Subscription will cancel on renewal",
    "cancels_at": "2026-10-15T10:00:00Z",
    "access_until": "2026-10-15T10:00:00Z"
  }
}

4. Get Billing History

GET /premium/history/

Description: Get billing history and invoices

Authentication: Required ✅

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "invoice_id": "inv_123456",
      "amount": 9.99,
      "currency": "USD",
      "date": "2026-01-15T10:00:00Z",
      "status": "paid",
      "description": "Premium Pro Monthly Subscription",
      "download_url": "https://cdn.linkedup.com/invoices/inv_123456.pdf"
    }
  ]
}

ANALYTICS Endpoints

Base URL: /api/v1/analytics/

1. Get Profile Impressions

GET /analytics/me/{username}/impressions/

Description: Get profile view impressions over time

Authentication: Required ✅ (Premium required)

Query Parameters:

Parameter Type Default Description
period string 30d 7d, 30d, 90d, 1y
grouping string day day, week, month

Response (200 OK):

{
  "success": true,
  "data": {
    "total_impressions": 5420,
    "average_daily": 180,
    "trend": "up",
    "trend_percentage": 15,
    "chart_data": [
      {
        "date": "2026-01-21",
        "impressions": 230,
        "unique_viewers": 180
      }
    ]
  }
}

2. Get Profile Views

GET /analytics/me/{username}/profile-views/

Description: Get profile view analytics

Authentication: Required ✅ (Premium required)

Query Parameters: Same as impressions

Response:

{
  "success": true,
  "data": {
    "total_views": 320,
    "unique_viewers": 250,
    "returning_rate": 22,
    "top_referrers": [
      {
        "source": "search",
        "views": 145
      }
    ]
  }
}

3. Get Post Engagement

GET /analytics/me/{username}/post-engagement/

Description: Get engagement analytics for all posts

Authentication: Required ✅ (Premium required)

Query Parameters: Same as impressions


4. Get Followers Growth

GET /analytics/me/{username}/followers-growth/

Description: Get followers growth over time

Authentication: Required ✅ (Premium required)


Best Practices

1. Authentication & Security

// ✅ DO: Store tokens securely
localStorage.setItem('accessToken', token);
localStorage.setItem('refreshToken', refreshToken);

// ❌ DON'T: Store sensitive data in localStorage
localStorage.setItem('password', password); // Never!

// ✅ DO: Always include Authorization header
const headers = {
  'Authorization': `Bearer ${accessToken}`,
  'Content-Type': 'application/json',
};

// ✅ DO: Handle token expiration
const response = await makeRequest('/api/endpoint');
if (response.status === 401) {
  await refreshToken();
  return retryRequest();
}

2. Error Handling

// ✅ DO: Comprehensive error handling
try {
  const response = await linkedUpAPI.post('/auth/login/', credentials);
  return response;
} catch (error) {
  if (error.status_code === 400) {
    // Validation error - show specific field errors
    displayFieldErrors(error.errors);
  } else if (error.status_code === 401) {
    // Authentication error
    redirectToLogin();
  } else if (error.status_code === 429) {
    // Rate limited
    showRateLimitMessage();
  } else {
    // Generic error
    showErrorMessage(error.message);
  }
}

// ✅ DO: Implement retry logic
async function makeRequestWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await delay(1000 * Math.pow(2, i)); // Exponential backoff
    }
  }
}

3. Performance

// ✅ DO: Implement pagination
async function loadUserPosts(username, page = 1) {
  const response = await linkedUpAPI.get(
    `/users/${username}/posts/?page=${page}&page_size=20`
  );
  return response;
}

// ✅ DO: Debounce search queries
let searchTimeout;
searchInput.addEventListener('input', (e) => {
  clearTimeout(searchTimeout);
  searchTimeout = setTimeout(() => {
    performSearch(e.target.value);
  }, 300);
});

// ✅ DO: Cache frequently accessed data
const cache = {};
async function getCachedUserProfile(username) {
  if (cache[username]) return cache[username];
  const profile = await linkedUpAPI.get(`/users/${username}/`);
  cache[username] = profile;
  return profile;
}

// ✅ DO: Implement infinite scroll efficiently
let isLoading = false;
window.addEventListener('scroll', async () => {
  if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 500) {
    if (!isLoading) {
      isLoading = true;
      await loadMorePosts();
      isLoading = false;
    }
  }
});

4. Real-time Updates

// ✅ DO: Use WebSocket for real-time notifications
const ws = new WebSocket(
  `wss://api.linkedup.com/ws/notifications/?token=${accessToken}`
);

ws.onmessage = (event) => {
  const notification = JSON.parse(event.data);
  showNotificationToast(notification);
  updateNotificationBadge();
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
  // Fallback to polling
  startPollingNotifications();
};

ws.onclose = () => {
  // Attempt reconnect
  setTimeout(() => {
    console.log('Reconnecting WebSocket...');
    initializeWebSocket();
  }, 5000);
};

// ✅ DO: Implement reconnection logic
function initializeWebSocket() {
  let reconnectAttempts = 0;
  const maxReconnectAttempts = 5;
  
  function connect() {
    ws = new WebSocket(wsUrl);
    
    ws.onopen = () => {
      reconnectAttempts = 0;
    };
    
    ws.onclose = () => {
      if (reconnectAttempts < maxReconnectAttempts) {
        reconnectAttempts++;
        const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
        setTimeout(connect, delay);
      }
    };
  }
  
  connect();
}

5. Data Validation

// ✅ DO: Validate input before sending
function validateEmail(email) {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

function validatePassword(password) {
  // At least 8 chars, uppercase, lowercase, number, special char
  const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
  return passwordRegex.test(password);
}

// ✅ DO: Handle API validation errors
try {
  await linkedUpAPI.post('/auth/register/', formData);
} catch (error) {
  if (error.errors) {
    // Server validation errors
    Object.entries(error.errors).forEach(([field, messages]) => {
      displayFieldError(field, messages[0]);
    });
  }
}

6. Rate Limiting

// ✅ DO: Implement request throttling
class ThrottledAPI {
  constructor(maxRequests = 100, timeWindow = 60000) {
    this.maxRequests = maxRequests;
    this.timeWindow = timeWindow;
    this.requests = [];
  }
  
  async request(method, url, body) {
    const now = Date.now();
    this.requests = this.requests.filter(t => now - t < this.timeWindow);
    
    if (this.requests.length >= this.maxRequests) {
      const waitTime = this.timeWindow - (now - this.requests[0]);
      await new Promise(resolve => setTimeout(resolve, waitTime));
      return this.request(method, url, body);
    }
    
    this.requests.push(now);
    return fetch(url, { method, body }).then(r => r.json());
  }
}

const throttledAPI = new ThrottledAPI(100, 60000);

7. TypeScript Definitions

// ✅ DO: Use TypeScript for type safety
interface User {
  user_id: number;
  username: string;
  email: string;
  profile_picture?: string;
  bio: string;
  followers_count: number;
  is_premium: boolean;
  created_at: string;
}

interface Post {
  post_id: number;
  post_uid: string;
  author: User;
  content: string;
  images: Image[];
  created_at: string;
  engagement: {
    likes_count: number;
    comments_count: number;
    shares_count: number;
  };
  is_liked: boolean;
}

interface APIResponse<T> {
  success: boolean;
  data: T;
  error?: {
    message: string;
    status_code: number;
    errors?: Record<string, string[]>;
  };
  pagination?: {
    page: number;
    page_size: number;
    total_count: number;
  };
}

async function getUser(username: string): Promise<User> {
  const response: APIResponse<User> = await linkedUpAPI.get(
    `/users/${username}/`
  );
  return response.data;
}

Common Issues & Solutions

Issue 1: "Token expired or invalid"

Cause: Access token has expired

Solution:

async function makeRequestWithAutoRefresh(method, endpoint, body) {
  try {
    return await linkedUpAPI.request(method, endpoint, body);
  } catch (error) {
    if (error.status_code === 401) {
      // Refresh token
      const refreshResponse = await linkedUpAPI.post('/auth/refresh/', {
        refresh_token: localStorage.getItem('refreshToken'),
      });
      
      linkedUpAPI.setToken(refreshResponse.access_token);
      localStorage.setItem('accessToken', refreshResponse.access_token);
      
      // Retry original request
      return linkedUpAPI.request(method, endpoint, body);
    }
    throw error;
  }
}

Issue 2: CORS Errors

Cause: Browser blocking cross-origin requests

Solution: Use proper CORS headers (must be configured on backend)

// Frontend: Ensure proper headers
const headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
};

// Backend should return:
// Access-Control-Allow-Origin: https://yourdomain.com
// Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
// Access-Control-Allow-Headers: Content-Type, Authorization

Issue 3: Network Timeout

Cause: Request taking too long

Solution:

async function requestWithTimeout(promise, timeoutMs = 10000) {
  const timeoutPromise = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Request timeout')), timeoutMs)
  );
  return Promise.race([promise, timeoutPromise]);
}

const response = await requestWithTimeout(
  linkedUpAPI.get('/slow-endpoint/'),
  5000
);

Issue 4: "Email already registered"

Cause: Duplicate email in system

Solution:

try {
  await linkedUpAPI.post('/auth/register/', {
    username: 'newuser',
    email: 'existing@example.com',
    password: 'Password123!',
  });
} catch (error) {
  if (error.status_code === 409) {
    showErrorMessage('Email already registered. Please login instead.');
  }
}

Issue 5: File Upload Errors

Cause: File too large or invalid format

Solution:

async function uploadProfilePicture(file) {
  // Validate file
  const maxSize = 5 * 1024 * 1024; // 5MB
  if (file.size > maxSize) {
    showErrorMessage('Image must be less than 5MB');
    return;
  }
  
  if (!['image/jpeg', 'image/png', 'image/webp'].includes(file.type)) {
    showErrorMessage('Only JPEG, PNG, and WebP supported');
    return;
  }
  
  // Upload
  const formData = new FormData();
  formData.append('profile_picture', file);
  
  try {
    const response = await fetch(
      `${linkedUpAPI.baseURL}/profile/me/${currentUsername}/`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${linkedUpAPI.token}`,
        },
        body: formData,
      }
    );
    return response.json();
  } catch (error) {
    console.error('Upload failed:', error);
  }
}

Testing Your API Integration

// Quick test suite
const apiTests = {
  async testAuth() {
    console.log('Testing authentication...');
    
    // Register
    const registerResp = await linkedUpAPI.post('/auth/register/', {
      username: 'testuser_' + Date.now(),
      email: `test_${Date.now()}@example.com`,
      password: 'TestPassword123!',
    });
    console.assert(registerResp.user_id, 'Registration failed');
    
    // Login
    const loginResp = await linkedUpAPI.post('/auth/login/', {
      email: registerResp.email,
      password: 'TestPassword123!',
    });
    console.assert(loginResp.access_token, 'Login failed');
    
    console.log('✅ Auth tests passed');
  },
  
  async testPosts() {
    console.log('Testing posts...');
    
    // Create post
    const postResp = await linkedUpAPI.post('/posts/', {
      content: 'Test post ' + Date.now(),
      visibility: 'public',
    });
    console.assert(postResp.post_uid, 'Post creation failed');
    
    // Get post
    const getResp = await linkedUpAPI.get(`/posts/${postResp.post_uid}/`);
    console.assert(getResp.post_id === postResp.post_id, 'Get post failed');
    
    console.log('✅ Post tests passed');
  },
};

// Run tests
await apiTests.testAuth();
await apiTests.testPosts();

API Rate Limits

  • Standard: 100 requests per minute per IP
  • Premium: 500 requests per minute per IP
  • Enterprise: Unlimited (custom SLA)

Rate Limit Headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642788000

Support & Contact


Last Updated: January 21, 2026
API Version: 1.0.0
Status: Production Ready