Skip to content

Instantly share code, notes, and snippets.

@nalgeon
Last active October 3, 2024 13:24
Show Gist options
  • Save nalgeon/547e95d4f6b7a8b06bcdc9de3c87856a to your computer and use it in GitHub Desktop.
Save nalgeon/547e95d4f6b7a8b06bcdc9de3c87856a to your computer and use it in GitHub Desktop.
Определить ИНН по паспортным данным человека

Определить ИНН по паспортным данным человека

В поддержку «Дадаты» часто обращаются с вопросом «как получить ИНН по паспортным данным». Налоговая служба предоставляет такой сервис, но без API.

В интернете есть несколько сайтов, которые предоставляют сервис «узнать ИНН» через API. Насколько нам известно, все они используют «неофициальный» интерфейс взаимодействия с налоговой, потому что ни официального API, ни открытых данных по ИНН не существует.

Мы в «Дадате» не хотим подключать неофициальное API налоговой: оно не отличается стабильностью работы и имеет непонятные перспективы. Если вы очень хотите получать ИНН через API — вызывайте API налоговой напрямую. Мы подготовили примеры, как это сделать на самых популярных языках — Python, PHP и JavaScript.

API налоговой бесплатное, но используете его вы на свой страх и риск. Никто не гарантирует, что оно будет работать корректно и стабильно.

Пример ответа API налоговой, если ИНН найден:

{'inn': 'xxxxxxxxxxxx', 'captchaRequired': False, 'code': 1}

Ответ API, если ИНН не найден:

{'captchaRequired': False, 'code': 0}

Примеры вызова на JS, PHP и Python — ниже.

const fetch = require("node-fetch");
function suggestInn(
surname,
name,
patronymic,
birthdate,
doctype,
docnumber,
docdate
) {
const url = "https://service.nalog.ru/inn-proc.do";
const data = {
fam: surname,
nam: name,
otch: patronymic,
bdate: birthdate,
bplace: "",
doctype: doctype,
docno: docnumber,
docdt: docdate,
c: "innMy",
captcha: "",
captchaToken: ""
};
const encoded = encode(data);
resp = fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: encoded
});
return resp;
}
function encode(data) {
encoded = Object.keys(data)
.map(
key => encodeURIComponent(key) + "=" + encodeURIComponent(data[key])
)
.join("&");
return encoded;
}
suggestInn(
"Иванов",
"Сергей",
"Владимирович",
"16.03.1982",
"21",
"45 12 229333",
"03.09.2012"
)
.then(response => {
return response.json();
})
.then(response => {
console.log(response);
});
<?php
function suggestInn($surname, $name, $patronymic, $birthdate, $doctype, $docnumber, $docdate)
{
$url = "https://service.nalog.ru/inn-proc.do";
$data = array(
"fam" => $surname,
"nam" => $name,
"otch" => $patronymic,
"bdate" => $birthdate,
"bplace" => "",
"doctype" => $doctype,
"docno" => $docnumber,
"docdt" => $docdate,
"c" => "innMy",
"captcha" => "",
"captchaToken" => ""
);
$options = array(
'http' => array(
'method' => 'POST',
'header' => array(
'Content-type: application/x-www-form-urlencoded',
),
'content' => http_build_query($data)
),
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
return $result;
}
$resp = suggestInn("Иванов", "Сергей", "Владимирович", "16.03.1982", "21", "45 12 229333", "03.09.2012");
print_r($resp);
import requests
from enum import Enum
class DocumentType(Enum):
# Паспорт гражданина СССР
passport_ussr = "01"
# Свидетельство о рождении
birth_certificate = "03"
# Паспорт иностранного гражданина
passport_foreign = "10"
# Вид на жительство в России
residence_permit = "12"
# Разрешение на временное проживание в России
residence_permit_temp = "15"
# Свидетельство о предоставлении временного убежища на территории России
asylum_certificate_temp = "19"
# Паспорт гражданина России
passport_russia = "21"
# Свидетельство о рождении, выданное уполномоченным органом иностранного государства
birth_certificate_foreign = "23"
# Вид на жительство иностранного гражданина
residence_permit_foreign = "62"
def suggest_inn(surname, name, patronymic, birthdate, doctype, docnumber, docdate):
url = "https://service.nalog.ru/inn-proc.do"
data = {
"fam": surname,
"nam": name,
"otch": patronymic,
"bdate": birthdate,
"bplace": "",
"doctype": doctype,
"docno": docnumber,
"docdt": docdate,
"c": "innMy",
"captcha": "",
"captchaToken": "",
}
resp = requests.post(url=url, data=data)
resp.raise_for_status()
return resp.json()
if __name__ == "__main__":
response = suggest_inn(
surname="Иванов",
name="Сергей",
patronymic="Владимирович",
birthdate="16.03.1982",
doctype=DocumentType.passport_russia.value,
docnumber="45 12 229333",
docdate="03.09.2012",
)
print(response)
@z7FooN
Copy link

z7FooN commented Jan 26, 2022

Здравствуйте. Скрипт перестал работать... (

@Ichinya
Copy link

Ichinya commented Mar 3, 2022

Здравствуйте. Скрипт перестал работать... (

На php работает, главное передавать данные, как в примере. Форматы паспорта и дат должны совпадать

@Vlasenkoip
Copy link

На пост запросы с некорректными данными получаю 400, с корректными CORS

@Ichinya
Copy link

Ichinya commented Jun 7, 2022

На пост запросы с некорректными данными получаю 400, с корректными CORS

Я проверил, скрипт работает. Проверял php.

@Avryanta
Copy link

Avryanta commented Jun 8, 2022

Спасибо! Обращаюсь HTTP-запросом из 1С - прекрасно работает.

@hyxp3r
Copy link

hyxp3r commented Jun 19, 2022

Всем привет! Проверяю на Python, по какой-то причине не находит некоторых людей, возврат {'captchaRequired': False, 'code': 0}
Руками проверяю, ИНН находит

@Ichinya
Copy link

Ichinya commented Jun 20, 2022

Всем привет! Проверяю на Python, по какой-то причине не находит некоторых людей, возврат {'captchaRequired': False, 'code': 0} Руками проверяю, ИНН находит

Тоже заметил. Причем выполняешь запрос, не нашел ИНН, но если выполнить позже то может найти. Я думаю, что ошибки могут быть из-за большой нагрузки на их сервер.

@hyxp3r
Copy link

hyxp3r commented Jun 20, 2022

Всем привет! Проверяю на Python, по какой-то причине не находит некоторых людей, возврат {'captchaRequired': False, 'code': 0} Руками проверяю, ИНН находит

Тоже заметил. Причем выполняешь запрос, не нашел ИНН, но если выполнить позже то может найти. Я думаю, что ошибки могут быть из-за большой нагрузки на их сервер.

Я поразбирался, у них поменялась схема запросов. Прилагаю свой код, Python

def check_inn(surname, name, patronymic, birthdate, doctype, docnumber, docdate):
    urlDo = "https://service.nalog.ru/inn-new-proc.do"
    urlJson = "https://service.nalog.ru/inn-new-proc.json"

    dataDo = {
        "c": "find",
        "captcha": "",
        "captchaToken": "",
        "fam": surname,
        "nam": name,
        "otch": patronymic,
        "bdate": birthdate,
        "doctype": doctype,
        "docno": docnumber,
        "docdt": docdate,
    }
    
    resp = requests.post(url=urlJson, data=dataDo).json()
    resp = resp['requestId']

    dataJson = {
        "c": "get",
        "requestId": resp, 
    }
    time.sleep(1)
    resp = requests.post(url=urlDo, data=dataJson).json()
    return resp

Я закинул обращение к функции в цикл, используя try, раз 5 пробую передать запрос, иногда нужно 3-4 попытки)
Иногда по новой схеме запросов не находит ИНН, а по старой (как тут прикрепил автор) находит. Тогда после 5 попыток в except закидываю вызов старого запроса

def check_inn_old(surname, name, patronymic, birthdate, doctype, docnumber, docdate):

    urlDo = "https://service.nalog.ru/inn-proc.do"
    data = {
        "fam": surname,
        "nam": name,
        "otch": patronymic,
        "bdate": birthdate,
        "bplace": "",
        "doctype": doctype,
        "docno": docnumber,
        "docdt": docdate,
        "c": "innMy",
        "captcha": "",
        "captchaToken": "",
    }
    resp = requests.post(url=urlDo, data=data)
    return resp.json()
def main():
    dfINN = pd.DataFrame(columns=['personalNumber','surname', 'name', 'patronymic', 'birthday', 'docnumber', 'docdate', 'INN'])
    dict, countRow = getSql()
    i = 0
    k = 0
    
    
    while i < countRow:
        try:
            response = check_inn(
            surname = dict['surname'][i],
            name = dict['name'][i],
            patronymic = dict['patronymic'][i],
            birthdate = dict['birthdate'][i],
            doctype = '21',
            docnumber = dict['docnumber'][i],
            docdate = dict['docdate'][i],
            )
            dfINN.loc[len(dfINN.index)] = [dict['personalNumber'][i], dict['surname'][i], dict['name'][i], dict['patronymic'][i], dict['birthdate'][i],
                dict['docnumber'][i], dict['docdate'][i], response['inn']] 
            print(f'Done {i}')
            k = 0
            i += 1
        except:
            k += 1
            print(f'Try again k={k}')
            if k > 5:
                try:
                    response = check_inn_old(
                    surname = dict['surname'][i],
                    name = dict['name'][i],
                    patronymic = dict['patronymic'][i],
                    birthdate = dict['birthdate'][i],
                    doctype = '21',
                    docnumber = dict['docnumber'][i],
                    docdate = dict['docdate'][i],
                    )
                    dfINN.loc[len(dfINN.index)] = [dict['personalNumber'][i], dict['surname'][i], dict['name'][i], dict['patronymic'][i], dict['birthdate'][i],
                    dict['docnumber'][i], dict['docdate'][i], response['inn']] 
                    print(f'Done old {i}')
                    i += 1
                except:
                    dfINN.loc[len(dfINN.index)] = [dict['personalNumber'][i], dict['surname'][i], dict['name'][i], dict['patronymic'][i], dict['birthdate'][i],
                    dict['docnumber'][i], dict['docdate'][i], 'Not found']  
                    i += 1  
        time.sleep(10)

Как-то так )

@andreevnickolay
Copy link

Спасибо! Обращаюсь HTTP-запросом из 1С - прекрасно работает.

Добрый день! Есть возможность увидеть результат реализации?

@Avryanta
Copy link

Avryanta commented Sep 16, 2022

Спасибо! Обращаюсь HTTP-запросом из 1С - прекрасно работает.

Добрый день! Есть возможность увидеть результат реализации?

Пожалуйста: https://paste1c.ru/8gvci6l5nci
Кстати, до сих пор работает

Процедура ЗаполнитьИННПоПаспортнымДаннымНаСервере()
	
	Если НЕ ЗначениеЗаполнено(Объект.Ссылка) Тогда
		Сообщить("Перед заполнением ИНН необходимо записать элемент справочника");
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос("ВЫБРАТЬ
	                      |	ПодтверждающиеДокументы.Владелец.Фамилия КАК Фамилия,
	                      |	ПодтверждающиеДокументы.Владелец.Имя КАК Имя,
	                      |	ПодтверждающиеДокументы.Владелец.Отчество КАК Отчество,
	                      |	ПодтверждающиеДокументы.Владелец.ДатаРождения КАК ДатаРождения,
	                      |	ПодтверждающиеДокументы.Серия КАК Серия,
	                      |	ПодтверждающиеДокументы.Номер КАК Номер,
	                      |	ПодтверждающиеДокументы.ДатаВыдачи КАК ДатаВыдачиПаспорта
	                      |ИЗ
	                      |	Справочник.ПодтверждающиеДокументы КАК ПодтверждающиеДокументы
	                      |ГДЕ
	                      |	ПодтверждающиеДокументы.ВидПодтверждающегоДокумента = ЗНАЧЕНИЕ(Перечисление.ВидыДокументов.Паспорт)
	                      |	И ПодтверждающиеДокументы.Владелец = &Владелец
	                      |	И НЕ ПодтверждающиеДокументы.ПометкаУдаления
	                      |	И ПодтверждающиеДокументы.Текущий");
	
	Запрос.УстановитьПараметр("Владелец", Объект.Ссылка);
	
	РезультатЗапроса = Запрос.Выполнить();
	
	Если РезультатЗапроса.Пустой() Тогда
		Сообщить("У данного контрагента не занесен паспорт! Заполнение ИНН невозможно");
		Возврат;
	КонецЕсли;
	
	Выборка = РезультатЗапроса.Выбрать();
	
	Если Выборка.Следующий() Тогда
		
		СоединениеHTTP = Новый HTTPСоединение("service.nalog.ru",443,,,,10, Новый ЗащищенноеСоединениеOpenSSL(,),Ложь);
		
		Заголовки = Новый Соответствие;
		Заголовки.Вставить("Content-type", "application/x-www-form-urlencoded");
		
		СерияПаспорта = СтрЗаменить(СокрЛП(Выборка.Серия), " ", "");
		
		СерияПаспорта = Лев(СерияПаспорта, 2) + " " + Прав(СерияПаспорта, 2);
		
		АдресРесурса = "/inn-proc.do?" + 
		"fam="     + СокрЛП(Выборка.Фамилия) + 
		"&nam="    + СокрЛП(Выборка.Имя) + 
		"&otch="   + СокрЛП(Выборка.Отчество) + 
		"&bdate="  + Формат(Выборка.ДатаРождения, "ДФ=dd.MM.yyyy") + // Формат даты: 04.12.1995 
		"&bplace"  +    // Оставляем пустым
		"&doctype=21" + // Так указывается тип Паспорт на сайте 
		"&docno="  + СерияПаспорта + " " + Выборка.Номер + // Строка вида "99 99 9999990" 
		"&docdt="  + Формат(Выборка.ДатаВыдачиПаспорта, "ДФ=dd.MM.yyyy") + // Формат даты: 04.12.1995 
		"&c=innMy" + 
		"&captcha" + 
		"&captchaToken";
		
		ЗапросHTTP = Новый HTTPЗапрос(АдресРесурса, Заголовки);
		
		Попытка
			
			Ответ = СоединениеHTTP.ОтправитьДляОбработки(ЗапросHTTP);
			
		Исключение
			
			Сообщить(ОписаниеОшибки());
			
			Возврат;
			
		КонецПопытки; 
		
		Если Ответ.КодСостояния = 200 Тогда
			
			ТелоОтвета = Ответ.ПолучитьТелоКакСтроку();
			СтруктураОтвета = СтруктураИзСтрокиJSON(ТелоОтвета);
			
			Если НЕ СтруктураОтвета = Неопределено Тогда
				
				Если СтруктураОтвета.code = 1 Тогда
					
					Объект.ИНН = СтруктураОтвета.inn;
					
				ИначеЕсли СтруктураОтвета.code = 0 Тогда
					
					Сообщить("По данным ФНС не найдена информация об ИНН.
					|Рекомендуется проверить правильность введенных данных и повторить попытку поиска.");
					
				КонецЕсли;
				
			КонецЕсли;
			
		Иначе
			
			Сообщить("Сервер вернул код состояния " + Ответ.КодСостояния + Символы.ПС + ТелоОтвета = Ответ.ПолучитьТелоКакСтроку());
			
		КонецЕсли;
		
	КонецЕсли;
	
КонецПроцедуры

Функция СтруктураИзСтрокиJSON(Строка, СвойстваТипаДата = Неопределено) Экспорт
	
	ЧтениеJSON = Новый ЧтениеJSON;
	ЧтениеJSON.УстановитьСтроку(Строка);
	
	Попытка
		ЗаписьJSON = ПрочитатьJSON(ЧтениеJSON, Ложь, СвойстваТипаДата, ФорматДатыJSON.ISO);
	Исключение
		Возврат Неопределено;
	КонецПопытки;
	
	Возврат ?(ТипЗнч(ЗаписьJSON) = Тип("Структура"), ЗаписьJSON, Неопределено); 
	
КонецФункции

@andreevnickolay
Copy link

Avryanta, благодарю!

@AndrewTishkin
Copy link

Такой сервис есть у Тинькофф
https://www.tinkoff.ru/inn/
Интересно, они его всё-таки реализовали через API от ФНС или "накостылили"?

Если через API, то у Dadata есть шанс на "легальный" путь)

@kireno
Copy link

kireno commented Dec 9, 2022

Спасибо! Обращаюсь HTTP-запросом из 1С - прекрасно работает.

Добрый день! Есть возможность увидеть результат реализации?

Пожалуйста: https://paste1c.ru/8gvci6l5nci Кстати, до сих пор работает

Процедура ЗаполнитьИННПоПаспортнымДаннымНаСервере()
	
	Если НЕ ЗначениеЗаполнено(Объект.Ссылка) Тогда
		Сообщить("Перед заполнением ИНН необходимо записать элемент справочника");
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос("ВЫБРАТЬ
	                      |	ПодтверждающиеДокументы.Владелец.Фамилия КАК Фамилия,
	                      |	ПодтверждающиеДокументы.Владелец.Имя КАК Имя,
	                      |	ПодтверждающиеДокументы.Владелец.Отчество КАК Отчество,
	                      |	ПодтверждающиеДокументы.Владелец.ДатаРождения КАК ДатаРождения,
	                      |	ПодтверждающиеДокументы.Серия КАК Серия,
	                      |	ПодтверждающиеДокументы.Номер КАК Номер,
	                      |	ПодтверждающиеДокументы.ДатаВыдачи КАК ДатаВыдачиПаспорта
	                      |ИЗ
	                      |	Справочник.ПодтверждающиеДокументы КАК ПодтверждающиеДокументы
	                      |ГДЕ
	                      |	ПодтверждающиеДокументы.ВидПодтверждающегоДокумента = ЗНАЧЕНИЕ(Перечисление.ВидыДокументов.Паспорт)
	                      |	И ПодтверждающиеДокументы.Владелец = &Владелец
	                      |	И НЕ ПодтверждающиеДокументы.ПометкаУдаления
	                      |	И ПодтверждающиеДокументы.Текущий");
	
	Запрос.УстановитьПараметр("Владелец", Объект.Ссылка);
	
	РезультатЗапроса = Запрос.Выполнить();
	
	Если РезультатЗапроса.Пустой() Тогда
		Сообщить("У данного контрагента не занесен паспорт! Заполнение ИНН невозможно");
		Возврат;
	КонецЕсли;
	
	Выборка = РезультатЗапроса.Выбрать();
	
	Если Выборка.Следующий() Тогда
		
		СоединениеHTTP = Новый HTTPСоединение("service.nalog.ru",443,,,,10, Новый ЗащищенноеСоединениеOpenSSL(,),Ложь);
		
		Заголовки = Новый Соответствие;
		Заголовки.Вставить("Content-type", "application/x-www-form-urlencoded");
		
		СерияПаспорта = СтрЗаменить(СокрЛП(Выборка.Серия), " ", "");
		
		СерияПаспорта = Лев(СерияПаспорта, 2) + " " + Прав(СерияПаспорта, 2);
		
		АдресРесурса = "/inn-proc.do?" + 
		"fam="     + СокрЛП(Выборка.Фамилия) + 
		"&nam="    + СокрЛП(Выборка.Имя) + 
		"&otch="   + СокрЛП(Выборка.Отчество) + 
		"&bdate="  + Формат(Выборка.ДатаРождения, "ДФ=dd.MM.yyyy") + // Формат даты: 04.12.1995 
		"&bplace"  +    // Оставляем пустым
		"&doctype=21" + // Так указывается тип Паспорт на сайте 
		"&docno="  + СерияПаспорта + " " + Выборка.Номер + // Строка вида "99 99 9999990" 
		"&docdt="  + Формат(Выборка.ДатаВыдачиПаспорта, "ДФ=dd.MM.yyyy") + // Формат даты: 04.12.1995 
		"&c=innMy" + 
		"&captcha" + 
		"&captchaToken";
		
		ЗапросHTTP = Новый HTTPЗапрос(АдресРесурса, Заголовки);
		
		Попытка
			
			Ответ = СоединениеHTTP.ОтправитьДляОбработки(ЗапросHTTP);
			
		Исключение
			
			Сообщить(ОписаниеОшибки());
			
			Возврат;
			
		КонецПопытки; 
		
		Если Ответ.КодСостояния = 200 Тогда
			
			ТелоОтвета = Ответ.ПолучитьТелоКакСтроку();
			СтруктураОтвета = СтруктураИзСтрокиJSON(ТелоОтвета);
			
			Если НЕ СтруктураОтвета = Неопределено Тогда
				
				Если СтруктураОтвета.code = 1 Тогда
					
					Объект.ИНН = СтруктураОтвета.inn;
					
				ИначеЕсли СтруктураОтвета.code = 0 Тогда
					
					Сообщить("По данным ФНС не найдена информация об ИНН.
					|Рекомендуется проверить правильность введенных данных и повторить попытку поиска.");
					
				КонецЕсли;
				
			КонецЕсли;
			
		Иначе
			
			Сообщить("Сервер вернул код состояния " + Ответ.КодСостояния + Символы.ПС + ТелоОтвета = Ответ.ПолучитьТелоКакСтроку());
			
		КонецЕсли;
		
	КонецЕсли;
	
КонецПроцедуры

Функция СтруктураИзСтрокиJSON(Строка, СвойстваТипаДата = Неопределено) Экспорт
	
	ЧтениеJSON = Новый ЧтениеJSON;
	ЧтениеJSON.УстановитьСтроку(Строка);
	
	Попытка
		ЗаписьJSON = ПрочитатьJSON(ЧтениеJSON, Ложь, СвойстваТипаДата, ФорматДатыJSON.ISO);
	Исключение
		Возврат Неопределено;
	КонецПопытки;
	
	Возврат ?(ТипЗнч(ЗаписьJSON) = Тип("Структура"), ЗаписьJSON, Неопределено); 
	
КонецФункции

Добрый день, а сейчас работает у вас?

@SergeyPominov
Copy link

SergeyPominov commented Dec 10, 2022

У меня скрипт последнее время перестал работать, но сегодня он выдал:
{"code":100,"captchaRequired":false}
Сталкивался кто то с такой проблемой, и если да то как ее решить?

@Avryanta
Copy link

@kireno @SergeyPominov Хмм, если перейти на https://service.nalog.ru/inn.do, то страница больше не доступна в принципе

@Trider12
Copy link

405 Not Allowed

@Ichinya
Copy link

Ichinya commented Feb 19, 2024

Такой сервис есть у Тинькофф https://www.tinkoff.ru/inn/ Интересно, они его всё-таки реализовали через API от ФНС или "накостылили"?

Спасибо за подсказку. Сделал у себя дополнительно через Тинькофф.

@aav1984
Copy link

aav1984 commented Feb 20, 2024

@Avryanta Спасибо за наводку и пример. Если что - до сих пор работает.
зы.: только лучше бы вы в тело упаковали перс.данные, от греха.

`Для каждого эл из СтруктураПараметров Цикл

	СтрокаЗапроса = эл.Ключ+"="+эл.Значение+"&"+СтрокаЗапроса; 

КонецЦикла;

СтрокаЗапроса = КодироватьСтроку(СтрокаЗапроса,СпособКодированияСтроки.URLВКодировкеURL);

HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса);`

Где в структуре ключ-значение параметров запроса (аналогично топик стартеру).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment