rynn-k / gists
coomer.js javascript
const axios = require('axios');
const crypto = require('crypto');

class Coomer {
    constructor() {
        this.SERVICES = ['onlyfans', 'fansly', 'candfans'];
    }
    
    getHeaders(referer = '') {
        const ip = [10, crypto.randomInt(256), crypto.randomInt(256), crypto.randomInt(256)].join('.');
        return {
            'user-agent': 'Mozilla/5.0 (Linux; Android 15; SM-F958 Build/AP3A.240905.015) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.86 Mobile Safari/537.36',
            ...(referer && { referer, accept: 'text/css' }),
            'x-forwarded-for': ip,
            'x-originating-ip': ip,
            'x-remote-ip': ip,
            'x-remote-addr': ip,
            'x-forwarded-host': ip,
            'x-connecting-ip': ip,
            'client-ip': ip,
            'x-client-ip': ip,
            'x-real-ip': ip,
            'x-forwarded-for-original': ip,
            'x-forwarded': ip,
            'x-cluster-client-ip': ip,
            'x-original-forwarded-for': ip
        };
    }
    
    popular = async function (page = 1) {
        try {
            if (isNaN(page)) throw new Error('Page must be a number.');
            
            const { data } = await axios.get(`https://coomer.st/api/v1/posts/popular?${page > 1 ? `o=${page * 50}&` : ''}period=recent`, {
                headers: this.getHeaders()
            });
            
            const totalPages = Math.floor(data.props.count / 50);
            if (parseInt(page) > totalPages) throw new Error(`Page ${page} exceeds total pages (${totalPages}).`);
            
            return {
                page: parseInt(page),
                total_pages: totalPages,
                posts: data.posts.filter(p => p.attachments.length > 0).map(p => ({
                    id: p.id,
                    title: p.title,
                    user_id: p.user,
                    service: p.service,
                    published: p.published,
                    attachments: p.attachments.map(a => 'https://n1.coomer.st/data' + a.path)
                }))
            };
        } catch (error) {
            throw new Error(error.message);
        }
    }
    
    search = async function (query, page = 1) {
        try {
            if (!query) throw new Error('Query is required.');
            if (isNaN(page)) throw new Error('Page must be a number.');
            
            const { data } = await axios.get(`https://coomer.st/api/v1/posts?${page > 1 ? `o=${page * 50}&` : ''}q=${encodeURIComponent(query)}`, {
                headers: this.getHeaders()
            });
            
            const totalPages = Math.floor(data.count / 50);
            if (parseInt(page) > totalPages) throw new Error(`Page ${page} exceeds total pages (${totalPages}).`);
            
            return {
                page: parseInt(page),
                total_pages: totalPages,
                posts: data.posts.filter(p => p.attachments.length > 0).map(p => ({
                    id: p.id,
                    title: p.title,
                    user_id: p.user,
                    service: p.service,
                    published: p.published,
                    attachments: p.attachments.map(a => 'https://n1.coomer.st/data' + a.path)
                }))
            };
        } catch (error) {
            throw new Error(error.message);
        }
    }
    
    profile = async function (id, service = 'onlyfans') {
        try {
            if (!id) throw new Error('Id must be a number.');
            if (!this.SERVICES.includes(service)) throw new Error(`Invalid service. Available: ${this.SERVICES.join(', ')}.`);
            
            const base = `https://coomer.st/api/v1/${service}/user/${id}`;
            const ref = `https://coomer.st/${service}/user/${id}`;
            
            const [{ data: profile }, { data: posts }] = await Promise.all([
                axios.get(`${base}/profile`, { headers: this.getHeaders(ref) }),
                axios.get(`${base}/posts`, { headers: this.getHeaders(ref) })
            ]);
            
            return {
                id: profile.id,
                name: profile.name,
                service: profile.service,
                public_id: profile.public_id,
                post_count: profile.post_count,
                updated: profile.updated,
                posts: posts.filter(p => p.attachments.length > 0).map(p => ({
                    id: p.id,
                    title: p.title,
                    published: p.published,
                    attachments: p.attachments.map(a => 'https://n1.coomer.st/data' + a.path)
                }))
            };
        } catch (error) {
            throw new Error(error.message);
        }
    }
}

// Usage:
const c = new Coomer();
c.search('girl').then(console.log);
4816 bytes ยท Updated Mar 21, 2026