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

class DeviantArt {
    constructor() {
        this.inst = axios.create({
            baseURL: 'https://www.deviantart.com',
            headers: {
                origin: 'https://www.deviantart.com',
                referer: 'https://www.deviantart.com/',
                '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'
            }
        });
    }
    
    getCsrf = async function () {
        try {
            const { data: html, headers } = await this.inst.get('/');
            
            const csrf = html.match(/window\.__CSRF_TOKEN__\s*=\s*'([^']*)'/)?.[1];
            if (!csrf) throw new Error('Failed to get csrf token.');
            
            const cookies = headers['set-cookie']?.join('; ') || '';
            this.inst.defaults.headers.common['cookie'] = cookies;
            
            return csrf;
        } catch (error) {
            throw new Error(error.message);
        }
    }
    
    search = async function ({ query, cursor } = {}) {
        try {
            if (!query) throw new Error('Query is required.');
            
            const csrf = await this.getCsrf();
            const { data } = await this.inst.get('/_puppy/dabrowse/search/all', {
                params: {
                    q: query,
                    da_minor_version: 20230710,
                    csrf_token: csrf,
                    ...(cursor && { cursor: cursor })
                }
            });
            
            return data;
        } catch (error) {
            throw new Error(error.message);
        }
    }
    
    detail = async function (url) {
        try {
            const match = url.match(/https?:\/\/(?:www\.)?deviantart\.com\/([^\/]+)\/(art|journal)\/[^\/]+-(\d+)/);
            if (!match) throw new Error('Invalid url.');
            
            const csrf = await this.getCsrf();
            const { data } = await this.inst.get('/_puppy/dadeviation/init', {
                params: {
                    deviationid: match[3],
                    username: match[1],
                    type: match[2],
                    include_session: false,
                    csrf_token: csrf,
                    expand: 'deviation.related',
                    preload: true,
                    da_minor_version: 20230710
                }
            });
            
            return data;
        } catch (error) {
            throw new Error(error.message);
        }
    }
}

// Usage:
const d = new DeviantArt();
d.search({ query: 'hutao' }).then(console.log);
2665 bytes ยท Updated Mar 5, 2026