├── .env.example ├── .gitignore ├── LICENSE ├── README.md ├── __tests__ └── index.js ├── instagram.js ├── package-lock.json └── package.json /.env.example: -------------------------------------------------------------------------------- 1 | USERNAME=ig_username 2 | PASSWORD=ig_password 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # instagram-nodejs 2 | Auth and get followers on instagram with nodejs 3 | 4 | Join us with gitter: https://gitter.im/nodejs-instagram/Library 5 | 6 | ### Important : you must update csrf token and sessionId only if password was changed 7 | 8 | ### To install from npm repository (I recommended use yarn, but you can use npm): 9 | ``` 10 | yarn add instagram-nodejs-without-api 11 | ``` 12 | 13 | ### You can get instagram followers with next code: 14 | ```js 15 | let Instagram = require('instagram-nodejs-without-api'); 16 | Instagram = new Instagram() 17 | 18 | 19 | Instagram.getCsrfToken().then((csrf) => 20 | { 21 | Instagram.csrfToken = csrf; 22 | }).then(() => 23 | { 24 | return Instagram.auth('inst-your-username', 'inst-your-password').then(sessionId => 25 | { 26 | Instagram.sessionId = sessionId 27 | 28 | return Instagram.getUserDataByUsername('username-for-get').then((t) => 29 | { 30 | return Instagram.getUserFollowers(t.graphql.user.id).then((t) => 31 | { 32 | console.log(t); // - instagram followers for user "username-for-get" 33 | }) 34 | }) 35 | 36 | }) 37 | }).catch(console.error); 38 | ``` 39 | 40 | ### Follow/unfollow 41 | ```js 42 | Inst = new Instagram() 43 | 44 | Inst.csrfToken = 'your-csrf' 45 | Inst.sessionId = 'your-session-id' 46 | Inst.follow(3,0) //follow "kevin" 47 | Inst.follow(3, 1) //unfollow "kevin" 48 | ```` 49 | 50 | ### Like/unlike 51 | ````js 52 | //get media id by url and like 53 | Insta.getMediaIdByUrl('https://www.instagram.com/p/BT1ynUvhvaR/').then(r => Insta.like(r).then(d => console.log(d))) 54 | //get media id by url and unlike 55 | Insta.getMediaIdByUrl('https://www.instagram.com/p/BT1ynUvhvaR/').then(r => Insta.unlike(r).then(d => console.log(d))) 56 | ```` 57 | 58 | ### Get feed 59 | ````js 60 | let pageFirst = Insta.getFeed(10).then(function(t) 61 | { 62 | let PageSecond = Insta.getFeed(10, Insta.getFeedNextPage(t)).then(function(t) 63 | { 64 | //two page 65 | console.log(t) 66 | }) 67 | }) 68 | ```` 69 | 70 | ### Get user media 71 | ````js 72 | //... auth (look up) 73 | //for example: get 12 first media entries for "kevin" 74 | // 0 - if you need to get first page 75 | // next cursor : r.page_info.end_cursor 76 | Insta.getUserMedia(3, '0', 12).then(f => console.log(f)) 77 | ```` 78 | 79 | ### Get media by hashtags and locations 80 | ````js 81 | Insta.commonSearch('Kyiv').then(r => 82 | { 83 | //get location id for Kyiv 84 | let locationId = r.places[0].place.location['pk'] 85 | //search posts from Kyiv 86 | Insta.searchBy('location', locationId, '0', 12).then(r => console.log(r)) 87 | }) 88 | //search posts by hashtag "Eurovision" 89 | Insta.searchBy('hashtag', 'Eurovision').then(r => console.log(r)) 90 | ```` 91 | 92 | When you pass items counter param instagram create pagination tokens on all iterations and gives on every response end_cursor, which the need to pass on next feed request 93 | 94 | 95 | You can get user id with Inst.getUserDataByUsername() method 96 | 97 | Star this repository on github, please. Thank you 98 | 99 | 100 | ### Tests 101 | 102 | You must define a .env file with username and password of the instagram login. (see .env.example) 103 | 104 | ``` npm test ``` 105 | 106 | ``` yarn test ``` -------------------------------------------------------------------------------- /__tests__/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const test = require('ava'); 3 | 4 | 5 | const username = process.env.USERNAME; 6 | const password = process.env.PASSWORD; 7 | 8 | console.log(username, password); 9 | 10 | test('user exist', t => { 11 | t.true(Boolean(username)); 12 | }); 13 | 14 | test('password exist', t => { 15 | t.true(Boolean(password)); 16 | }); 17 | 18 | test('Get Instagram Account', async t => { 19 | const InstagraApi = require('../instagram.js'); 20 | const Instagram = new InstagraApi(); 21 | 22 | Instagram.csrfToken = await Instagram.getCsrfToken(); 23 | Instagram.sessionId = await Instagram.auth(username, password); 24 | 25 | const data = await Instagram.getUserDataByUsername('getnerdify'); 26 | const userId = data.graphql.user.id; 27 | 28 | t.is(userId, '7696780203'); 29 | }); 30 | -------------------------------------------------------------------------------- /instagram.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | "use-strict"; 4 | 5 | const fetch = require('node-fetch'); 6 | const formData = require('form-data'); 7 | 8 | module.exports = class Instagram { 9 | /** 10 | * Constructor 11 | */ 12 | constructor(csrfToken, sessionId) { 13 | this.csrfToken = csrfToken 14 | this.sessionId = sessionId 15 | this.userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' 16 | this.userIdFollowers = {}; 17 | this.timeoutForCounter = 250 18 | this.timeoutForCounterValue = 20000 19 | this.paginationDelay = 20000 20 | this.receivePromises = {} 21 | this.searchTypes = ['location', 'hashtag'] 22 | 23 | this.essentialValues = { 24 | sessionid : undefined, 25 | ds_user_id : undefined, 26 | csrftoken : undefined, 27 | shbid : undefined, 28 | rur : undefined, 29 | mid : undefined, 30 | shbts : undefined, 31 | mcd : undefined, 32 | ig_cb : 1, 33 | //urlgen : undefined //this needs to be filled in according to my RE 34 | }; 35 | 36 | this.baseHeader = { 37 | 'accept-langauge': 'en-US;q=0.9,en;q=0.8,es;q=0.7', 38 | 'origin': 'https://www.instagram.com', 39 | 'referer': 'https://www.instagram.com/', 40 | 'upgrade-insecure-requests': '1', 41 | 'user-agent': this.userAgent, 42 | } 43 | } 44 | 45 | 46 | generateCookie(simple){ 47 | if (simple) return 'ig_cb=1' 48 | 49 | var cookie = '' 50 | var keys = Object.keys(this.essentialValues) 51 | for (var i = 0; i < keys.length; i++){ 52 | var key = keys[i]; 53 | if (this.essentialValues[key] !== undefined) { 54 | cookie += key + '=' + this.essentialValues[key] + (i < keys.length - 1 ? '; ' : '') 55 | } 56 | } 57 | 58 | return cookie; 59 | } 60 | 61 | combineWithBaseHeader(data){ 62 | return Object.assign(this.baseHeader, data) 63 | } 64 | 65 | updateEssentialValues(src, isHTML){ 66 | //assumes that essential values will be extracted from a cookie unless specified by the isHTML bool 67 | 68 | if (!isHTML){ 69 | var keys = Object.keys(this.essentialValues) 70 | 71 | for (var i = 0; i < keys.length; i++){ 72 | var key = keys[i]; 73 | if (!this.essentialValues[key]) 74 | for (let cookie in src) 75 | if (src[cookie].includes(key) && !src[cookie].includes(key + '=""')){ 76 | var cookieValue = src[cookie].split(';')[0].replace(key + '=', '') 77 | this.essentialValues[key] = cookieValue 78 | break; 79 | } 80 | } 81 | } else { 82 | var subStr = src; 83 | 84 | var startStr = '') - 1); 89 | 90 | var json = JSON.parse(subStr); 91 | 92 | this.essentialValues.csrftoken = json.config.csrf_token; 93 | this.rollout_hash = json.rollout_hash; 94 | } 95 | } 96 | 97 | /** 98 | * User data by username 99 | * @param {String} username 100 | * @return {Object} Promise 101 | */ 102 | getUserDataByUsername(username) { 103 | 104 | var fetch_data = { 105 | 'method': 'get', 106 | 'headers': 107 | this.combineWithBaseHeader( 108 | { 109 | 'accept': 'text/html,application/xhtml+xml,application/xml;q0.9,image/webp,image/apng,*.*;q=0.8', 110 | 'accept-encoding': 'gzip, deflate, br', 111 | 'cookie': this.generateCookie() 112 | } 113 | ) 114 | } 115 | 116 | return fetch('https://www.instagram.com/' + username, fetch_data).then(res => res.text().then(function (data) { 117 | const regex = /window\._sharedData = (.*);<\/script>/; 118 | const match = regex.exec(data); 119 | 120 | if (!match || (match && typeof match[1] === 'undefined')) { 121 | return ''; 122 | } 123 | 124 | return JSON.parse(match[1]).entry_data.ProfilePage[0]; 125 | })) 126 | } 127 | 128 | /** 129 | Is private check 130 | * @param {String} username 131 | */ 132 | isPrivate(username) { 133 | return this.getUserDataByUsername(username).then((data) => 134 | data.user.is_private 135 | ) 136 | } 137 | 138 | /** 139 | * User followers list 140 | * Bench - 1k followers/1 min 141 | * @param {Int} userId 142 | * @param {String} endCursor cursor used to fetch next page 143 | * @param {Int} count count of results to return (API may return less) 144 | * @param {Int} followersCounter counter of followers 145 | * @param {Boolean} selfSelf if call by self 146 | * @return {Object} array followers list 147 | */ 148 | getUserFollowers(userId, endCursor, count, followersCounter, selfSelf) { 149 | const self = this 150 | 151 | if (!selfSelf) 152 | self.userIdFollowers[userId] = [] 153 | 154 | if (typeof self.receivePromises[userId] !== 'undefined' && !selfSelf) 155 | return 0 156 | 157 | count = count || 20; 158 | 159 | const query = { 160 | id: userId, 161 | include_reel: true, 162 | fetch_mutual: true, 163 | first: count 164 | }; 165 | if (endCursor) { 166 | query.after = endCursor; 167 | } 168 | 169 | const variables = encodeURIComponent(JSON.stringify(query)); 170 | 171 | self.receivePromises[userId] = 1 172 | return fetch('https://www.instagram.com/graphql/query/?query_hash=56066f031e6239f35a904ac20c9f37d9&variables=' + variables, 173 | { 174 | 'method': 'get', 175 | 'headers': 176 | this.combineWithBaseHeader( 177 | { 178 | 'accept': 'text/html,application/xhtml+xml,application/xml;q0.9,image/webp,image/apng,*.*;q=0.8', 179 | 'accept-encoding': 'gzip, deflate, br', 180 | 'cookie': this.generateCookie() 181 | } 182 | ) 183 | }).then(res => { 184 | return res.text().then((response) => { 185 | //prepare convert to json 186 | let json = response; 187 | 188 | try { 189 | json = JSON.parse(response) 190 | } 191 | catch (e) { 192 | console.log('Session error') 193 | console.log(response) 194 | return []; 195 | } 196 | 197 | if (json.status == 'ok') { 198 | self.userIdFollowers[userId] = self.userIdFollowers[userId].concat(json.data.user.edge_followed_by.edges) 199 | 200 | if (json.data.user.edge_followed_by.page_info.has_next_page) { 201 | let end_cursor = json.data.user.edge_followed_by.page_info.end_cursor 202 | return new Promise((resolve) => { 203 | console.log('fetching next page in ' + this.paginationDelay / 1000 + ' seconds'); 204 | setTimeout(() => { 205 | resolve(self.getUserFollowers(userId, end_cursor, count, 1, 1)); 206 | }, this.paginationDelay); 207 | }); 208 | } 209 | else { 210 | self.receivePromises[userId] = undefined 211 | return self.userIdFollowers[userId] 212 | } 213 | 214 | } 215 | else { 216 | return new Promise((resolve) => { 217 | console.log(json); 218 | console.log('request failed, retrying in ' + this.paginationDelay / 1000 + ' seconds'); 219 | setTimeout(() => { 220 | resolve(self.getUserFollowers(userId, endCursor, count, followersCounter, selfSelf)); 221 | }, this.paginationDelay); 222 | }); 223 | } 224 | 225 | }).catch((e) => { 226 | console.log('Instagram returned:' + e) 227 | }) 228 | }) 229 | } 230 | 231 | /** 232 | * Get csrf token 233 | * @return {Object} Promise 234 | */ 235 | getCsrfToken() { 236 | return fetch('https://www.instagram.com', 237 | { 238 | 'method': 'get', 239 | 'headers': 240 | this.combineWithBaseHeader( 241 | { 242 | 'accept': 'text/html,application/xhtml+xml,application/xml;q0.9,image/webp,image/apng,*.*;q=0.8', 243 | 'accept-encoding': 'gzip, deflate, br', 244 | 'cookie': this.generateCookie(true) 245 | } 246 | ) 247 | }).then( t => { 248 | this.updateEssentialValues(t.headers._headers['set-cookie']) 249 | return t.text() 250 | }).then( html => { 251 | this.updateEssentialValues(html, true) 252 | return this.essentialValues.csrftoken 253 | }).catch(() => 254 | console.log('Failed to get instagram csrf token') 255 | ) 256 | } 257 | 258 | /** 259 | * Session id by usrname and password 260 | * @param {String} username 261 | * @param {String} password 262 | * @return {Object} Promise 263 | */ 264 | auth(username, password) { 265 | var formdata = 'username=' + username + '&password=' + password + '&queryParams=%7B%7D' 266 | 267 | var options = { 268 | method : 'POST', 269 | body : formdata, 270 | headers : 271 | this.combineWithBaseHeader( 272 | { 273 | 'accept' : '*/*', 274 | 'accept-encoding' : 'gzip, deflate, br', 275 | 'content-length' : formdata.length, 276 | 'content-type' : 'application/x-www-form-urlencoded', 277 | 'cookie' : 'ig_cb=' + this.essentialValues.ig_cb, 278 | 'x-csrftoken' : this.csrfToken, 279 | 'x-instagram-ajax' : this.rollout_hash, 280 | 'x-requested-with' : 'XMLHttpRequest', 281 | } 282 | ) 283 | } 284 | 285 | return fetch('https://www.instagram.com/accounts/login/ajax/', options).then( 286 | t => { 287 | this.updateEssentialValues(t.headers._headers['set-cookie']) 288 | return this.essentialValues.sessionid; 289 | }).catch(() => 290 | console.log('Instagram authentication failed (challenge required erro)') 291 | ) 292 | } 293 | 294 | /** 295 | * Registration for instagram, returning true or false 296 | * true if account was successfully created 297 | * @param {String} username 298 | * @param {String} password 299 | * @param {String} name 300 | * @param {String} email 301 | * @return {Boolen} account_created 302 | */ 303 | reg(username, password, name, email) { 304 | let form = new formData(); 305 | form.append('username', username) 306 | form.append('password', password) 307 | form.append('firstname', name) 308 | form.append('email', email) 309 | form.append('seamless_login_enabled', "1") 310 | 311 | return fetch('https://www.instagram.com/accounts/web_create_ajax/', { 312 | 'method': 'post', 313 | 'body': form, 314 | 'headers': { 315 | 'referer': 'https://www.instagram.com/', 316 | 'origin': 'https://www.instagram.com', 317 | 'user-agent': this.userAgent, 318 | 'x-instagram-ajax': '1', 319 | 'x-requested-with': 'XMLHttpRequest', 320 | 'x-csrftoken': this.csrfToken, 321 | cookie: 'csrftoken=' + this.csrfToken 322 | } 323 | }) 324 | .then(res => res.json()) 325 | .then(json => { 326 | return json.account_created; 327 | }) 328 | .catch(() => console.log('Instagram registration failed')) 329 | } 330 | 331 | 332 | /** 333 | * I did not want to implement this, but I need a stars on github 334 | * If you use this library - star this rep https://github.com/yatsenkolesh/instagram-nodejs 335 | * Thank you, bro 336 | * Follow/unfollow user by id 337 | * @param {int} userID 338 | * @param {boolean} isUnfollow 339 | * @return {object} Promise of fetch request 340 | */ 341 | follow(userId, isUnfollow) { 342 | const headers = 343 | { 344 | 'referer': 'https://www.instagram.com/', 345 | 'origin': 'https://www.instagram.com', 346 | 'user-agent': this.userAgent, 347 | 'x-instagram-ajax': '1', 348 | 'content-type': 'application/json', 349 | 'x-requested-with': 'XMLHttpRequest', 350 | 'x-csrftoken': undefined, 351 | cookie: ' sessionid=' + this.sessionId + '; csrftoken=' + this.csrfToken + '; mid=WPL0LQAEAAGG3XL5-xHXzClnpqA3; rur=ASH; mid=WRN1_AAEAAE07QksztCl3OCnLj8Y;' 352 | } 353 | 354 | return fetch('https://www.instagram.com/web/friendships/' + userId + (isUnfollow == 1 ? '/unfollow' : '/follow'), 355 | { 356 | 'method': 'post', 357 | 'headers': this.getHeaders()//headers 358 | }).then(res => { 359 | return res 360 | }) 361 | } 362 | 363 | /** 364 | * @return {Object} default headers 365 | */ 366 | getHeaders() { 367 | return { 368 | 'referer': 'https://www.instagram.com/p/BT1ynUvhvaR/?taken-by=yatsenkolesh', 369 | 'origin': 'https://www.instagram.com', 370 | 'user-agent': this.userAgent, 371 | 'x-instagram-ajax': '1', 372 | 'x-requested-with': 'XMLHttpRequest', 373 | 'x-csrftoken': this.csrfToken, 374 | cookie: ' sessionid=' + this.sessionId + '; csrftoken=' + this.csrfToken + ';' 375 | } 376 | } 377 | 378 | /** 379 | * Return user data by id 380 | * @param {Int} id 381 | * @return {Object} promise 382 | */ 383 | getUserDataById(id) { 384 | let query = 'ig_user(' + id + '){id,username,external_url,full_name,profile_pic_url,biography,followed_by{count},follows{count},media{count},is_private,is_verified}' 385 | 386 | let form = new formData(); 387 | form.append('q', query) 388 | 389 | return fetch('https://www.instagram.com/query/', 390 | { 391 | 'method': 'post', 392 | 'body': form, 393 | 'headers': this.getHeaders() 394 | }).then(res => 395 | res.json().then(t => t) 396 | ) 397 | } 398 | 399 | /** 400 | * When you pass items counter param instagram create pagination 401 | * tokens on all iterations and gives on every response end_cursor, which the need to pass on next feed request 402 | * 403 | * This method return first "items" posts of feed 404 | * Coming soon will be opportunity for get part of feed 405 | * On testing stage (+- all rights) 406 | * If you have a problems - create issue : https://github.com/yatsenkolesh/instagram-nodejs 407 | * @param {Int} items (default - 10) 408 | * @return {Object} Promise 409 | */ 410 | getFeed(items, cursor) { 411 | items = items ? items : 10; 412 | return fetch('https://www.instagram.com/graphql/query/?query_id=17866917712078875&fetch_media_item_count=' + items + '&fetch_media_item_cursor=' + cursor + '&fetch_comment_count=4&fetch_like=10', 413 | { 414 | headers: this.getHeaders(), 415 | }).then(t => 416 | t.json().then(r => r) 417 | ) 418 | } 419 | 420 | /** 421 | * Simple variable for get next page 422 | * @param {Object} json contents from this.getFeed 423 | * @return {String} if next page is not exists - false 424 | */ 425 | getFeedNextPage(json) { 426 | let page = json.data.user.edge_web_feed_timeline.page_info 427 | 428 | return page.has_next_page ? page.end_cursor : false 429 | } 430 | 431 | /** 432 | * Attention: postId need transfer only as String (reason int have max value - 2147483647) 433 | * @example postID - '1510335854710027921' 434 | * @param {String} post id 435 | * @return {Object} Promse 436 | */ 437 | like(postId) { 438 | return fetch('https://www.instagram.com/web/likes/' + postId + '/like/', 439 | { 440 | 'method': 'POST', 441 | 'headers': this.getHeaders() 442 | }).then(t => 443 | t.json().then(r => r) 444 | ) 445 | } 446 | 447 | /** 448 | * Attention: postId need transfer only as String (reason int have max value - 2147483647) 449 | * @example postID - '1510335854710027921' 450 | * @param {String} postId 451 | * @return {Object} Promse 452 | */ 453 | unlike(postId) { 454 | return fetch('https://www.instagram.com/web/likes/' + postId + '/unlike/', 455 | { 456 | 'method': 'POST', 457 | 'headers': this.getHeaders() 458 | }).then(t => 459 | t.json().then(r => r) 460 | ) 461 | } 462 | 463 | 464 | /** 465 | * @example url = https://www.instagram.com/p/BT1ynUvhvaR/ 466 | * @param {String} url 467 | * @return {Object} Promise 468 | */ 469 | getMediaInfoByUrl(url) { 470 | return fetch('https://api.instagram.com/oembed/?url=' + url, 471 | { 472 | 'headers': this.getHeaders() 473 | }).then(t => t.json().then(r => r)) 474 | } 475 | 476 | /** 477 | * @example url = https://www.instagram.com/p/BT1ynUvhvaR/ 478 | * @param {String} url 479 | * @return {Object} Promise 480 | */ 481 | getMediaIdByUrl(url) { 482 | return this.getMediaInfoByUrl(url).then(t => t.media_id.split('_')[0]) 483 | } 484 | 485 | /** 486 | * Get media user list on userId with pagination 487 | * @param {String} userId 488 | * @param {String} cursor (next cursor). Use 0, if you want to get first page 489 | * @param {Int} mediaCounter default - 12 490 | * @return {Object} Promise 491 | */ 492 | getUserMedia(userId, cursor, mediaCounter) { 493 | cursor = cursor ? cursor : '0' 494 | mediaCounter = mediaCounter ? mediaCounter : 12 495 | let form = new formData() 496 | form.append('q', 'ig_user(' + userId + ') { media.after(' + cursor + ', ' + mediaCounter + ') {\ 497 | count,\ 498 | nodes {\ 499 | __typename,\ 500 | caption,\ 501 | code,\ 502 | comments {\ 503 | count\ 504 | },\ 505 | comments_disabled,\ 506 | date,\ 507 | dimensions {\ 508 | height,\ 509 | width\ 510 | },\ 511 | display_src,\ 512 | id,\ 513 | is_video,\ 514 | likes {\ 515 | count\ 516 | },\ 517 | owner {\ 518 | id\ 519 | },\ 520 | thumbnail_src,\ 521 | video_views\ 522 | },\ 523 | page_info\ 524 | }\ 525 | }') 526 | form.append('ref', 'users::show') 527 | form.append('query_id', '17849115430193904') // this is static id. May be changed after rebuild, but now actually 528 | 529 | return fetch('https://www.instagram.com/query/', 530 | { 531 | headers: this.getHeaders(), 532 | method: 'post', 533 | body: form 534 | }).then(r => r.text().then(t => t)) 535 | } 536 | 537 | /** 538 | * End cursor - t.entry_data.TagPage[0].tag.media.page_info['end_cursor'] 539 | * Media(nodes) - t.entry_data.TagPage[0].tag.media['nodes'] 540 | * @param {String} searchBy - location, hashtag 541 | * @param {String} q - location id, or hashtag 542 | * @param {String} cursor pagination cursor 543 | * @param {Int} mediaCounter 544 | * @return {Object} Promise 545 | */ 546 | searchBy(searchBy, q, cursor, mediaCounter) { 547 | if (this.searchTypes.indexOf(searchBy) === false) 548 | throw 'search type ' + searchBy + ' is not found' 549 | 550 | //exclusion for hashtag if not cursor 551 | if (searchBy == 'hashtag' && !cursor) { 552 | return fetch('https://www.instagram.com/explore/tags/' + q + '/', 553 | { 554 | headers: this.getHeaders(), 555 | }).then(t => t.text().then(r => JSON.parse(r.match(/\